asp做静态网站,网页设计有什么用,网站设计制作的特点有哪些,网页游戏不用登录文章目录 MyBatis特性下载持久化层技术对比 搭建MyBatis创建maven工程创建MyBatis的核心配置文件创建mapper接口创建MyBatis的映射文件测试功能加入log4j日志功能加入log4j的配置文件 核心配置文件的完善与详解MyBatis的增删改查测试功能 MyBatis获取参数值在IDEA中设置中配置文… 文章目录 MyBatis特性下载持久化层技术对比 搭建MyBatis创建maven工程创建MyBatis的核心配置文件创建mapper接口创建MyBatis的映射文件测试功能加入log4j日志功能加入log4j的配置文件 核心配置文件的完善与详解MyBatis的增删改查测试功能 MyBatis获取参数值在IDEA中设置中配置文件的模板操作步骤操作步骤 MyBatis获取参数值的两种方式(重点)获取单个字面量类型的参数获取多个字面量类型的参数获取map集合类型的参数获取实体类类型的参数使用Param注解标识参数(常用) MyBatis的各种查询功能查询单个实体类对象查询一个list集合查询单个数据查询一条数据为Map集合查询多条数据为Map集合用Map的list集合接收用MapKey注解设置键(Key) 特殊SQL的执行模糊查询批量删除动态设置表名添加功能获取自增的主键 自定义映射resultMap多对一映射处理级联方式处理映射关系使用association处理映射关系分步查询 一对多映射处理通过collection解决分步查询 动态SQlif标签where标签trim标签choose、when、otherwise标签foreach标签通过数组实现批量删除通过集合实现批量添加 sql标签 MyBatis的缓存一级缓存缓存失效 二级缓存开启条件缓存失效相关设置 缓存查询的顺序第三方缓存EHCache添加依赖 MyBatis
MyBatis最初是Apache的一个开源项目iBatis,2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。
iBatis一词来源于internet和abatis的组合是一个基于Java的持久层框架。iBatis提供的持久层框架包括SQLMaps和Data Access Obiects(DAO)。
特性
MyBatis是(支持定制化SQL、存储过程以及高级映射的优秀的)持久层框架(本质)MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集MyBatis可以使用简单的XML或注解用于配置和原始映射将接口和Java的POJO(Plain Old Java Objects普通的Java对象)映射成数据库中的记录MyBatis是一个半自动的ORM(Object Relation Mapping)框架
下载
MyBatis官网下载地址https://github.com/mybatis/mybatis-3
打开链接之后往下拉找到这个地方 点进去之后下载jar包(主体) 下载完之后其中有mybatis的jar包已经官方文档pdf(方便学习后续会使用到)。
或者如果嫌上面下载的方式太麻烦则可以采用maven依赖项式的下载将下面代码导入依赖配置中即可。
!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --
dependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.13/version
/dependency持久化层技术对比 JDBC SQL夹杂在Java代码中耦合度高导致硬编码(将代码写死)内伤维护不易且实际开发需求中 SQL 有变化频繁修改的情况多见代码冗长开发效率低 Hibernate和JPA 操作简便开发效率高程序中的长难复杂 SQL 需要绕过框架内部自动生产的 SQL不容易做特殊优化基于全映射的全自动框架大量字段的 POJO 进行部分映射时比较困难。反射操作太多导致数据库性能下降 MyBatis 轻量级性能出色SQL 和 Java 编码分开功能边界清晰。Java代码专注业务、SQL语句专注数据开发效率稍逊于HIbernate
搭建MyBatis
首先创建一个空项目然后再下面创建一个maven工程
创建maven工程
注意这里的打包方式要是jar方式。
下面将引入依赖(pom.xml)
dependencies!-- Mybatis核心 --dependencygroupIdorg.mybatis/groupIdartifactIdmybatis/artifactIdversion3.5.13/version/dependency!-- junit测试 --dependencygroupIdjunit/groupIdartifactIdjunit/artifactIdversion4.13.2/versionscopetest/scope/dependency!-- MySQL驱动 --dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.32/version/dependency
/dependencies依赖引入完成后则需要配置一下MyBatis的核心配置文件
创建MyBatis的核心配置文件
在main包下的resources路径下创建一个mybatisConfig.xml文件然后进行配置。
这时就需要用到mybatis中的官方文档了将其中的的配置文件直接复制过来即可。
?xml version1.0 encodingUTF-8 ?
!DOCTYPE configuration
PUBLIC -//mybatis.org//DTD Config 3.0//EN
http://mybatis.org/dtd/mybatis-3-config.dtd
configuration!--设置连接数据库的环境--environments defaultdevelopmentenvironment iddevelopmenttransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver valuecom.mysql.jdbc.Driver/property nameurlvaluejdbc:mysql://localhost:3306/MyBatis/property nameusername valueroot/property namepassword value123456//dataSource/environment/environments!--引入映射文件--mappersmapper resourcemappers/UserMapper.xml//mappers
/configuration这里用到了mysql中的账号和密码需要改成自己mysql上对应的账号和密码。
创建mapper接口
该接口相当于以前的DAO但是mapper仅仅是一个接口不需要提供实现类。
当然在此之前需要在mysql中创建上述配置中的数据库并且创建一张user表创建代码如下。
CREATE DATABASE MyBatis;USE Mybatis;CREATE TABLE t_user(id INT NOT NULL AUTO_INCREMENT, username VARCHAR(20), password VARCHAR(20), age INT, sex CHAR, email VARCHAR(20), PRIMARY KEY (id)
)ENGINEINNODB CHARSETutf8 COLLATEutf8_general_ci;接下来创建User的实体类(POJO)
package code;/*** 创建人 HeXin* 所属包 User* 所属项目 MyBatis* 创建时间 2023/3/30 20:38* 描述 User实体类*/
public class User {private Integer id;private String username;private String password;private Integer age;private String sex;private String email;public User () {}public User (Integer id, String username, String password, Integer age, String sex, String email) {this.id id;this.username username;this.password password;this.age age;this.sex sex;this.email email;}public Integer getId () {return id;}public void setId (Integer id) {this.id id;}public String getUsername () {return username;}public void setUsername (String username) {this.username username;}public String getPassword () {return password;}public void setPassword (String password) {this.password password;}public Integer getAge () {return age;}public void setAge (Integer age) {this.age age;}public String getSex () {return sex;}public void setSex (String sex) {this.sex sex;}public String getEmail () {return email;}public void setEmail (String email) {this.email email;}Overridepublic String toString () {return User{ id id , username username \ , password password \ , age age , sex sex \ , email email \ };}
}创建User的mapper接口在里面定义了一个添加用户信息的方法
package code.mapper;/*** 创建人 HeXin* 所属包 UserMapper* 所属项目 MyBatis* 创建时间 2023/3/30 20:40* 描述 User类的Mapper接口*/
public interface UserMapper {/*** Description: 添加用户名信息* CreateTime: 2023/3/30 21:41* Author: HeXin*/int addUser();
}创建MyBatis的映射文件
这里要引入一个概念对象关系映射(ORMObject Relationship Mapping)
其中的对象是指的实体类对象关系是关系型数据库映射为二者之间的对应关系。
Java概念数据库概念类表属性列/字段对象记录/行
在main中的resources中创建一个mappers软件包并在此创建一个UserMapper.xml配置文件。
查询官方文档找到关于映射文件的配置部分将其拷贝过来并在其中实现插入一条用户信息的sql。
?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapperPUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecode.mapper.UserMapper!--对应方法int addUser();--insert idaddUserinsert into t_user values(null,xiaoming,123456,19,男,xiaomingqq.com);/insert
/mapper创建映射文件的注意事项(重要)
映射文件的命名规则表所对应的实体类的类名Mapper.xml(如表为t_user映射的实体类为User因此所对应的映射文件为UserMapper.xml)。因此一个映射文件对应一个实体类对应一张表的操作
MyBatis映射文件用于编写SQL访问以及操作表中的数据。MyBatis映射文件存放的位置为src/main/resources/mappers目录下。
MyBatis中可以面向接口操作数据要保证两个一致 mapper接口的全类名和映射文件的命名空间(namespace)保持一致。mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致。
测试功能
在test包下创建TestMyBatis测试类并实现测试方法
Test
public void testMyBatis(){InputStream stream null;try {//加载核心配置文件stream Resources.getResourceAsStream(mybatisConfig.xml);} catch (IOException e) {e.printStackTrace();}//获取SqlSessionFactoryBuilderSqlSessionFactoryBuilder builder new SqlSessionFactoryBuilder();//获取sqlSessionFactorySqlSessionFactory build builder.build(stream);//获取SqlSessionSqlSession sqlSession build.openSession();//获取mapper接口对象UserMapper mapper sqlSession.getMapper(UserMapper.class);//测试功能int i mapper.addUser();//提交事务sqlSession.commit();System.out.println(受影响行数i);
}将代码中的一些参数进行解读
SqlSession代表Java程序和数据库之间的会话。(就如同HttpSession是Java程序和浏览器之间的会话)SqlSessionFactory是“生产”SqlSession的“工厂”。
其中存在着工厂模式(如果创建某一个对象使用的过程基本固定那么我们就可以把创建这个对象的相关代码封装到一个“工厂类”中以后都使用这个工厂类来“生产”我们需要的对象)。
注意当测试功能实现后一定要进行事务提交否则数据库那边会没有数据显示因为SqlSessionFactory的openSession方法中有一个autoCommit参数默认为false也就是默认不自动提交事务。
若需要自动提交事务可将其参数值改为true(此时自己写的提交事务就可以注释掉了)当需要手动进行提交事务的时候直接使用默认情况即可。
//获取SqlSession
SqlSession sqlSession build.openSession(true);加入log4j日志功能
首先要加入相对应依赖
!-- log4j日志 --
dependencygroupIdlog4j/groupIdartifactIdlog4j/artifactIdversion1.2.17/version
/dependency加入log4j的配置文件
创建一个名为log4j.xml的配置文件其存放在main下的resources目录下
?xml version1.0 encodingUTF-8 ?
!DOCTYPE log4j:configuration SYSTEM log4j.dtd
log4j:configuration xmlns:log4jhttp://jakarta.apache.org/log4j/appender nameSTDOUT classorg.apache.log4j.ConsoleAppenderparam nameEncoding valueUTF-8 /layout classorg.apache.log4j.PatternLayoutparam nameConversionPattern value%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n //layout/appenderlogger namejava.sqllevel valuedebug //loggerlogger nameorg.apache.ibatislevel valueinfo //loggerrootlevel valuedebug /appender-ref refSTDOUT //root
/log4j:configuration日记级别 FATAL(致命)ERROR(错误)WARN(警告)INFO(信息)DEBUG(调试)
从左到右打印的内容越来越详细
核心配置文件的完善与详解
这里了解一下即可现在使用时将其拷贝过去即可以后在ssm整合的环境中是可以没有核心配置文件的因为核心配置文件中的所有内容都可以交给spring处理。
?xml version1.0 encodingUTF-8 ?
!DOCTYPE configurationPUBLIC -//MyBatis.org//DTD Config 3.0//ENhttp://MyBatis.org/dtd/MyBatis-3-config.dtd
configuration!--引入properties文件此时就可以${属性名}的方式访问属性值--properties resourcejdbc.properties/propertiessettings!--将表中字段的下划线自动转换为驼峰--setting namemapUnderscoreToCamelCase valuetrue/!--开启延迟加载--setting namelazyLoadingEnabled valuetrue//settingstypeAliases!--typeAlias设置某个具体的类型的别名属性type需要设置别名的类型的全类名alias设置此类型的别名若不设置此属性该类型拥有默认的别名即类名且不区分大小写若设置此属性此时该类型的别名只能使用alias所设置的值每个实体类就需要设置一个typeAlias比较麻烦所以使用频率较低--
!-- typeAlias typecode.User aliasUser/typeAlias--!--以包为单位设置该包下所有的类型都拥有默认的别名即类名且不区分大小写--package namecode//typeAliases!--environments设置多个连接数据库的环境属性default设置默认使用的环境的id--environments defaultmysql_test!--environment设置具体的连接数据库的环境信息属性id设置环境的唯一标识可通过environments标签中的default设置某一个环境的id表示默认使用的环境--environment idmysql_test!--transactionManager设置事务管理方式属性type设置事务管理方式typeJDBC|MANAGEDtypeJDBC设置当前环境的事务管理都必须手动处理typeMANAGED设置事务被管理例如spring中的AOP--transactionManager typeJDBC/!--dataSource设置数据源属性type设置数据源的类型typePOOLED|UNPOOLED|JNDItypePOOLED使用数据库连接池即会将创建的连接进行缓存下次使用可以从缓存中直接获取不需要重新创建typeUNPOOLED不使用数据库连接池即每次使用连接都需要重新创建typeJNDI调用上下文中的数据源--dataSource typePOOLED!--设置驱动类的全类名--property namedriver value${jdbc.driver}/!--设置连接数据库的连接地址--property nameurl value${jdbc.url}/!--设置连接数据库的用户名--property nameusername value${jdbc.username}/!--设置连接数据库的密码--property namepassword value${jdbc.password}//dataSource/environment/environments!--引入映射文件--mappers
!-- mapper resourcemappers/UserMapper.xml/--!--以包为单位将包下所有的映射文件引入核心配置文件注意此方式必须保证mapper接口和mapper映射文件必须在相同的包下--package namecode.mappers//mappers
/configuration到此目前需要用到的核心配置文件已经配置完成下面将开始MyBatis的CRUD功能(增删改查)
MyBatis的增删改查 添加 !--int addUser();--
insert idaddUserinsert into t_user values(null,xiaohong,123456,19,女,xiaohongqq.com);
/insert删除 !--void deleteUser()--
delete iddeleteUserdelete from t_user where id4;
/delete修改 !--void updateUser()--
update idupdateUserupdate t_user set username lihua,sex 男 where id6;
/update查询 !--查询功能标签必须设置resultType(设置默认映射关系)或resultMap(设置自定义映射关系)--
!--User getUserById()--
select idgetUserById resultTypeUserselect * from t_user where id5;
/select
!--ListUser getUsers()--
select idgetUsers resultTypeUserselect * from t_user;
/select测试功能
Test
public void testAdd(){InputStream stream null;try {//加载核心配置文件stream Resources.getResourceAsStream(mybatisConfig.xml);} catch (IOException e) {e.printStackTrace();}//获取SqlSessionFactoryBuilderSqlSessionFactoryBuilder builder new SqlSessionFactoryBuilder();//获取sqlSessionFactorySqlSessionFactory build builder.build(stream);//获取SqlSessionSqlSession sqlSession build.openSession(true);//获取mapper接口对象UserMapper mapper sqlSession.getMapper(UserMapper.class);//测试功能int i mapper.addUser();System.out.println(受影响行数i);
}
Test
public void testUpdate(){InputStream stream null;try {stream Resources.getResourceAsStream(mybatisConfig.xml);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory build new SqlSessionFactoryBuilder().build(stream);SqlSession sqlSession build.openSession(true);UserMapper mapper sqlSession.getMapper(UserMapper.class);mapper.updateUser();
}
Test
public void testDelete(){InputStream stream null;try {stream Resources.getResourceAsStream(mybatisConfig.xml);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory build new SqlSessionFactoryBuilder().build(stream);SqlSession sqlSession build.openSession(true);UserMapper mapper sqlSession.getMapper(UserMapper.class);mapper.deleteUser();
}
Test
public void testSelect(){InputStream stream null;try {stream Resources.getResourceAsStream(mybatisConfig.xml);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory build new SqlSessionFactoryBuilder().build(stream);SqlSession sqlSession build.openSession(true);UserMapper mapper sqlSession.getMapper(UserMapper.class);//根据id查询用户信息System.out.println(mapper.getUserById());System.out.println(--------------------------------);//查询所有用户信息ListUser users mapper.getUsers();users.forEach(user - System.out.println(user));
}到此MyBatis最基础的框架与功能已经实现~ MyBatis获取参数值
MyBatis获取参数值有两种方式一种是${}一种是#{}
${}的本质是使用字符串拼接的方式拼接sql若为字符串类型或日期类型的字段进行赋值时则需要手动加单引号。
#{}的本质是占位符赋值此时为字符串类型或日期类型的字段进行赋值时可以自动添加单引号。
此时新建一个模板重新搭建一个MyBatis框架。因为其核心配置文件每次都去官方文档拷贝实在太麻烦所以这里使用IDEA中的代码模板功能后续需要核心配置文件和映射文件时直接可以一键生成。
在IDEA中设置中配置文件的模板
在idea中配置核心配置文件模板
操作步骤 第一步 第二步 将简单的模板代码拷贝进模板文件主体中
?xml version1.0 encodingUTF-8 ?
!DOCTYPE configurationPUBLIC -//MyBatis.org//DTD Config 3.0//ENhttp://MyBatis.org/dtd/MyBatis-3-config.dtd
configurationproperties resourcejdbc.properties/propertiessettingssetting namemapUnderscoreToCamelCase valuetrue/setting namelazyLoadingEnabled valuetrue//settingstypeAliasespackage name//typeAliasesenvironments defaultmysql_testenvironment idmysql_testtransactionManager typeJDBC/dataSource typePOOLEDproperty namedriver value${jdbc.driver}/property nameurl value${jdbc.url}/property nameusername value${jdbc.username}/property namepassword value${jdbc.password}//dataSource/environment/environmentsmapperspackage name//mappers
/configuration在idea中配置映射文件模版
操作步骤
第一步与配置核心文件时一样区别在于第二步 第二步 MyBatis获取参数值的两种方式(重点)
获取单个字面量类型的参数
若mapper接口中的方法参数为单个的字面量类型 此时可以使用${}和#{}以任意的名称获取参数的值注意${}需要手动加单引号。
首先因为每次测试时都需要创建的sqlsession存在大量一样的代码所以将其提取出来形成一个工具类叫做SqlSessionUtils
package 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;/*** 创建人 HeXin* 所属包 SqlSessionUtils* 所属项目 MyBatis* 创建时间 2023/4/1 10:08* 描述 获取SqlSession的工具类*/
public class SqlSessionUtils {public static SqlSession getSqlSession(){InputStream stream null;try {stream Resources.getResourceAsStream(mybatisConfig.xml);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory build new SqlSessionFactoryBuilder().build(stream);SqlSession sqlSession build.openSession(true);return sqlSession;}
}下面将用代码实现这两种方式获取参数 #{} 查询代码
!--User getUserByUsername(String username)--
select idgetUserByUsername resultTypeUserselect * from t_user where username #{username};
/select测试代码
Test
public void testSingle(){SqlSession sqlSession SqlSessionUtils.getSqlSession();UserMapper mapper sqlSession.getMapper(UserMapper.class);User user mapper.getUserByUsername(xiaohong);System.out.println(user);
}测试的结果 从测试的结果可以看出#{}的确使用的?占位符赋值获取参数 ${} 查询代码
!--User getUserByUsername(String username)--
select idgetUserByUsername resultTypeUserselect * from t_user where username ${username};
/select测试代码
Test
public void testSingle(){SqlSession sqlSession SqlSessionUtils.getSqlSession();UserMapper mapper sqlSession.getMapper(UserMapper.class);User user mapper.getUserByUsername(xiaohong);System.out.println(user);
}测试的结果 从测试结果可以看出${}使用的是字符串拼接的方式进行赋值
值得注意的时因为${}不会自动添加单引号所以当使用${}时一定要记得用单引号将其进行包围否则就会报出一下异常 获取多个字面量类型的参数
若mapper接口中的方法参数为多个时 此时MyBatis会自动将这些参数放在一个map集合中以arg0,arg1…为键以参数为值以 param1,param2…为键以参数为值因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值。由于上面获取单个参数已经演示过${}和#{}的使用和区别下面就演示其中一种。 代码演示 查询代码(错误方式)
!--User login(String username, String password)--
select idlogin resultTypeUserselect * from t_user where username #{username} and password #{password};
/select测试代码
Test
public void testLogin(){SqlSession sqlSession SqlSessionUtils.getSqlSession();UserMapper mapper sqlSession.getMapper(UserMapper.class);User user mapper.login(xiaohong, 123456);System.out.println(user);
}当有多个参数时不能像单个参数一样直接使用形参名获取否则会报异常 上面的错误信息中不仅告诉了哪里出错还给出了解决方案。
查询代码(正确方式)
!--User login(String username, String password)--
select idlogin resultTypeUserselect * from t_user where username #{arg0} and password #{arg1};
/select测试结果 获取map集合类型的参数
若mapper接口中的方法需要的参数为多个时此时可以手动创建map集合将这些数据放在map中 只需要通过${}或#{}访问map集合的键就可以获取相对应的值。此方式由获取多参数方式演变而来相当于自己设置键来访问数据。 代码实现 查询代码
!--User login(MapString,Object map)--
select idlogin resultTypeUserselect * from t_user where username #{username} and password #{password};
/select测试代码
Test
public void testLogin(){SqlSession sqlSession SqlSessionUtils.getSqlSession();UserMapper mapper sqlSession.getMapper(UserMapper.class);MapString,Object map new HashMap();map.put(username,xiaohong);map.put(password,123456);User user mapper.login(map);System.out.println(user);
}测试结果与获取多参数的结果一样
获取实体类类型的参数
若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{}通过访问实体类对象中的属性名获取属性值。 代码演示 向表中插入数据
!--int addUser(User user)--
insert idaddUserinsert intot_user values(null,#{username},#{password},#{age},#{sex},#{email})
/insert测试代码
Test
public void testAdd(){SqlSession sqlSession SqlSessionUtils.getSqlSession();UserMapper mapper sqlSession.getMapper(UserMapper.class);int i mapper.addUser(new User(null, xiaoming, 123456, 19, 男, xiaomingqq.com));System.out.println(i);
}测试结果 使用Param注解标识参数(常用)
此方式可以将第一种(获取单个参数)、第二种(获取多个参数)、第三种(获取map类型)方式整合成一种方式都可以使用Param进行实现获取参数。
此时会将这些参数放在map集合中以Param注解的value属性值为键以参数为值以param1,param2…为键以参数为值只需要通过${}和#{}访问map集合的键就可以获取相对应的值。 代码演示 查询代码
!--User login(Param(username)String username, Param(password)String password)--
select idlogin resultTypeUserselect * from t_user where username #{username} and password #{password};
/select测试代码
Test
public void testLogin(){SqlSession sqlSession SqlSessionUtils.getSqlSession();UserMapper mapper sqlSession.getMapper(UserMapper.class);User user mapper.login(xiaohong, 123456);System.out.println(user);
}测试结果 MyBatis的各种查询功能
共分为五种查询情况查询单个实体类对象、查询一个list集合、查询单个数据、查询一条数据为map集合查询多条数据为map集合。下面将逐个进行学习
查询单个实体类对象
这种查询情况已经实现过很多次了这里就不多赘述直接上代码
/*** Description:根据id查询用户信息* CreateTime: 2023/4/1 12:00* Author: HeXin*/
User getUserById(Param(id) Integer id);查询语句
!--User getUserById(Integer id);--
select idgetUserById resultTypeUserselect * from t_user where id #{id};
/select测试代码
Test
public void testGetUser(){SqlSession sqlSession SqlSessionUtils.getSqlSession();SelectMapper mapper sqlSession.getMapper(SelectMapper.class);User user mapper.getUserById(5);System.out.println(user);
}测试结果 查询一个list集合
这种情况可以包含第一种情况因为list集合中可以不包含任何数据也可以只包含一条数据。
/*** Description: 查询所有用户信息* CreateTime: 2023/4/1 12:10* Author: HeXin*/
ListUser getUsers();查询语句
!--ListUser getUsers();--
select idgetUsers resultTypeUserselect * from t_user;
/select测试代码
Test
public void testGetUsers(){SqlSession sqlSession SqlSessionUtils.getSqlSession();SelectMapper mapper sqlSession.getMapper(SelectMapper.class);ListUser users mapper.getUsers();users.forEach(user - System.out.println(user));
}测试结果 查询单个数据
单个数据一般是指单行单列的数据如一张表上的总记录数、统计总共的金额等等。
下面将以查询用户信息的总记录数为例进行学习 代码演示 /*** Description: 查询用户的总记录数* CreateTime: 2023/4/1 14:48* Author: HeXin*/
Integer getCount();查询语句(MyBatis设置的有默认的类型别名所以这里的resultType设置为全类名或别名都行。)
!--Integer getCount();--
select idgetCount resultTypeIntegerselect count(*) from t_user;
/select测试代码
Test
public void testGetCount(){SqlSession sqlSession SqlSessionUtils.getSqlSession();SelectMapper mapper sqlSession.getMapper(SelectMapper.class);Integer count mapper.getCount();System.out.println(总记录数count);
}测试结果为 通过查找官方文档我们可以得知MyBatis设置的默认全类名有以下这些 查询一条数据为Map集合
就是将查询到的一条数据存储到Map集合中。 代码演示 /*** Description: 根据id查询用户信息为一个map集合* CreateTime: 2023/4/1 15:07* Author: HeXin*/
MapString,Object getUserByIdToMap(Param(id) Integer id);查询语句
!--MapString,Object getUserByIdToMap(Param(id) Integer id);--
select idgetUserByIdToMap resultTypemapselect * from t_user where id #{id};
/select测试代码
Test
public void testGetUserToMap(){SqlSession sqlSession SqlSessionUtils.getSqlSession();SelectMapper mapper sqlSession.getMapper(SelectMapper.class);MapString, Object userMap mapper.getUserByIdToMap(6);System.out.println(userMap);
}测试结果 查询多条数据为Map集合
将查询到的多个数据都存储在Map集合中。这种方式以后的使用频率是很高的因为map可以转换成json对象然后将数据传递给前端。注意这里的多条数据不能只用一个Map来接收应该用一个Map的LIst集合来接收(或者用MapKey设置一个键(Key)此时就可以将每条数据转换的Map集合作为值以某个字段的值作为主键放在同一个Map集合中)否则就会报异常(此异常与用一个参数接收多条数据时的异常一样) 代码演示 用Map的list集合接收
/*** Description: 查询所有用户信息为Map集合* CreateTime: 2023/4/1 15:18* Author: HeXin*/
ListMapString,Object getUsersToMap();查询语句
!--MapString,Object getUsersToMap();--
select idgetUsersToMap resultTypemapselect * from t_user;
/select测试代码
Test
public void testGetUsersToMap() {SqlSession sqlSession SqlSessionUtils.getSqlSession();SelectMapper mapper sqlSession.getMapper(SelectMapper.class);ListMapString, Object users mapper.getUsersToMap();users.forEach(user- System.out.println(user));
}测试结果 用MapKey注解设置键(Key)
/*** Description: 查询所有用户信息为Map集合* CreateTime: 2023/4/1 15:18* Author: HeXin*/
MapKey(id)
MapInteger,Object getUsersToMap();查询语句
!--MapString,Object getUsersToMap();--
select idgetUsersToMap resultTypemapselect * from t_user;
/select测试代码
Test
public void testGetUsersToMap() {SqlSession sqlSession SqlSessionUtils.getSqlSession();SelectMapper mapper sqlSession.getMapper(SelectMapper.class);MapInteger, Object users mapper.getUsersToMap();users.forEach((id,user)- System.out.println(keyid user));
}测试结果 到此MyBatis中的各种查询功能已经学习完毕。
特殊SQL的执行
模糊查询
这里就存在一个问题因为#{}是占位符赋值所以会将sql语句中的单引号部分内容替换成问号并且会自动加上单引号导致查询失败所以这个时候有三种解决办法下面进行学习
以根据用户名模糊查询为例
/*** Description: 根据用户名进行模糊查询* CreateTime: 2023/4/1 15:52* Author: HeXin*/
ListUser getUserByLike(Param(username)String username);使用${}获取参数值
!--ListUser getUserByLike(Param(username)String username)--
select idgetUserByLike resultTypeUserselect * from t_user where username like %${username}%
/select使用#{}与SQL中concat字符串拼接函数结合使用它
!--ListUser getUserByLike(Param(username)String username)--
select idgetUserByLike resultTypeUserselect * from t_user where username like concat(%,#{username},%)
/select使用%“#{}”%去拼接其中的内容(最常用的方式)
!--ListUser getUserByLike(Param(username)String username)--
select idgetUserByLike resultTypeUserselect * from t_user where username like %#{username}%
/select测试代码
Test
public void testGetUserByLike(){SqlSession sqlSession SqlSessionUtils.getSqlSession();SQLMapper mapper sqlSession.getMapper(SQLMapper.class);ListUser user mapper.getUserByLike(xiao);user.forEach(u- System.out.println(u));
}测试结果 批量删除
/*** Description: 批量删除* CreateTime: 2023/4/1 16:11* Author: HeXin*/
int deleteMore(Param(ids) String ids);SQL语句
!--int deleteMore(Param(ids),String ids);--
delete iddeleteMoredelete from t_user where id in(${ids})
/delete测试代码
Test
public void testDeleteMore(){SqlSession sqlSession SqlSessionUtils.getSqlSession();SQLMapper mapper sqlSession.getMapper(SQLMapper.class);int i mapper.deleteMore(5,6,7);System.out.println(受影响行数i);
}测试结果 动态设置表名
根据表名来查询数据当然这里也应该使用${}来获取参数
/*** Description: 查询指定表中的数据* CreateTime: 2023/4/1 16:29* Author: HeXin*/
ListUser getUserByTableName(Param(tableName) String tableName);查询语句
!--ListUser getUserByTableName(Param(tableName) String tableName);--
select idgetUserByTableName resultTypeUserselect * from ${tableName};
/select测试代码
Test
public void testGetUserByTableName(){SqlSession sqlSession SqlSessionUtils.getSqlSession();SQLMapper mapper sqlSession.getMapper(SQLMapper.class);ListUser users mapper.getUserByTableName(t_user);users.forEach(user- System.out.println(user));
}测试结果 添加功能获取自增的主键
这是没有添加获取自增主键的添加用户信息打印结果很显然其id值为null 因为增删改有统一的返回值是受影响的行数 因此只能将获取的自增的主键放在传输的参数user对象的某个属性中。
/*** Description: 添加用户信息* CreateTime: 2023/4/1 17:14* Author: HeXin*/
void addUser(User user);添加语句
!--void addUser();--
!--useGeneratedKeys设置使用自增的主键keyProperty因为增删改有统一的返回值是受影响的行数因此只能将获取的自增的主键放在传输的参数user对象的某个属性中--
insert idaddUser useGeneratedKeystrue keyPropertyidinsert into t_user values(null,#{username},#{password},#{age},#{sex},#{email});
/insert测试代码
Test
public void testAddUser(){SqlSession sqlSession SqlSessionUtils.getSqlSession();SQLMapper mapper sqlSession.getMapper(SQLMapper.class);User user new User(null,WLM,1122334,18,女,wlmsina.com);mapper.addUser(user);System.out.println(user);
}测试结果 这次添加其id值就不是null了。
自定义映射resultMap
resultMap设置自定义映射
这里将会用到多对一或一对多的关系所以需要再创建两张表。
CREATE TABLE t_emp( eid INT NOT NULL AUTO_INCREMENT, emp_name VARCHAR(20), age INT, sex CHAR, email VARCHAR(20),did INT,PRIMARY KEY (eid)
) ENGINEINNODB CHARSETutf8 COLLATEutf8_general_ci;CREATE TABLE t_dept( did INT NOT NULL AUTO_INCREMENT, dept_name VARCHAR(20), PRIMARY KEY (did)
) ENGINEINNODB CHARSETutf8 COLLATEutf8_general_ci;向表中插入数据 t_emp INSERT INTO mybatis.t_emp (eid, emp_name, age, sex, email, did)
VALUES (1, xiaoming, 18, 男, xiaomingqq.com, 1);
INSERT INTO mybatis.t_emp (eid, emp_name, age, sex, email, did)
VALUES (2, xiaohong, 19, 女, xiaohongqq.com, 1);
INSERT INTO mybatis.t_emp (eid, emp_name, age, sex, email, did)
VALUES (3, lihua, 22, 男, lihuasina.com, 2);
INSERT INTO mybatis.t_emp (eid, emp_name, age, sex, email, did)
VALUES (4, wangqiang, 21, 男, liuqiang163.com, 3);t_dept INSERT INTO mybatis.t_dept (did, dept_name) VALUES (1, A);
INSERT INTO mybatis.t_dept (did, dept_name) VALUES (2, B);
INSERT INTO mybatis.t_dept (did, dept_name) VALUES (3, C); 对应的创建两个表的实体类
package POJO;/*** 创建人 HeXin* 所属包 Emp* 所属项目 MyBatis* 创建时间 2023/4/1 18:00* 描述*/
public class Emp {private Integer eid;private String empName;private Integer age;private String sex;private String email;private Dept dept;public Dept getDept () {return dept;}public void setDept (Dept dept) {this.dept dept;}public Emp (Integer eid, String empName, Integer age, String sex, String email) {this.eid eid;this.empName empName;this.age age;this.sex sex;this.email email;}public Emp () {}public Integer getEid () {return eid;}public void setEid (Integer eid) {this.eid eid;}public String getEmpName () {return empName;}public void setEmpName (String empName) {this.empName empName;}public Integer getAge () {return age;}public void setAge (Integer age) {this.age age;}public String getSex () {return sex;}public void setSex (String sex) {this.sex sex;}public String getEmail () {return email;}public void setEmail (String email) {this.email email;}Overridepublic String toString () {return Emp{ eid eid , empName empName \ , age age , sex sex \ , email email \ , dept dept };}
}package POJO;import java.util.List;/*** 创建人 HeXin* 所属包 Dept* 所属项目 MyBatis* 创建时间 2023/4/1 18:02* 描述*/
public class Dept {private Integer did;private String deptName;private ListEmp emps;public ListEmp getEmps () {return emps;}public void setEmps (ListEmp emps) {this.emps emps;}public Dept (Integer did, String deptName) {this.did did;this.deptName deptName;}public Dept () {}public Integer getDid () {return did;}public void setDid (Integer did) {this.did did;}public String getDeptName () {return deptName;}public void setDeptName (String deptName) {this.deptName deptName;}Overridepublic String toString () {return Dept{ did did , deptName deptName \ , emps emps };}
}若字段名与实体类中的属性名不一致则通过查询语句查询数据的时候是无法查到的这是因为字段名有自己的风格要求(下划线)属性名也有风格要求(驼峰)。解决此问题共有三个办法 给查询的时候给字段名起别名将别名设置为驼峰风格的。(实现起来比较麻烦尤其是当字段名很多的时候非常消耗时间)。 通过设置MyBatis核心配置文件设置全局变量将下划线自动映射为驼峰。该配置已经在完善核心配置文件时已经涉及到了。 !--设置全局变量--
settings!--将表中字段的下划线自动转换为驼峰--setting namemapUnderscoreToCamelCase valuetrue/
/settings通过resultMap设置自定义的映射关系 !--resultMap设置自定义映射属性id表示自定义映射的唯一标识type查询的数据要映射的实体类的类型子标签id设置主键的映射关系result设置普通字段的映射关系association设置多对一的映射关系collection设置一对多的映射关系属性property设置映射关系中实体类中的属性名column设置映射关系中表中的字段名--
resultMap iduserMap typeUserid propertyid columnid/idresult propertyuserName columnuser_name/resultresult propertypassword columnpassword/resultresult propertyage columnage/resultresult propertysex columnsex/result
/resultMap
!--ListEmp getEmps--
select idgetEmps resultMapuserMapselect * from t_emp;
/select多对一映射处理
一共有三种处理方式。通过查询员工信息及其对应部门信息为例。
/*** Description: 查询员工及其对应部门信息* CreateTime: 2023/4/2 10:33* Author: HeXin*/
Emp getEmpAndDept(Param(eid) Integer eid);级联方式处理映射关系
查询语句
resultMap idempAndDeptResultMap typeEmpid propertyeid columneid/idresult propertyempName columnemp_name/resultresult propertyage columnage/resultresult propertysex columnsex/resultresult propertyemail columnemail/resultresult propertydept.did columndid/resultresult propertydept.deptName columndept_name/result
/resultMap
!--Emp getEmpAndDept(Param(eid) Integer eid);--
select idgetEmpAndDept resultMapempAndDeptResultMapselect * from t_emp left join t_dept on t_emp.did t_dept.did where t_emp.eid #{eid};
/select使用association处理映射关系
查询语句
resultMap idempAndDeptResultMap typeEmpid propertyeid columneid/idresult propertyempName columnemp_name/result propertyage columnage/resultresult propertysex columnsex/resultresult propertyemail columnemail/result!--association处理多对一的映射关系property需要处理多对的映射关系的属性名javaType该属性的类型--association propertydept javaTypeDeptid propertydid columndid/idresult propertydeptName columndept_name//association
/resultMap
!--Emp getEmpAndDept(Param(eid) Integer eid);--
select idgetEmpAndDept resultMapempAndDeptResultMapselect * from t_emp left join t_dept on t_emp.did t_dept.did where t_emp.eid #{eid};
/select分步查询
先查询员工的信息
/*** Description: 查询员工信息* CreateTime: 2023/4/2 11:32* Author: HeXin*/
Emp getEmpByStep(Param(eid) int eid);查询语句
resultMap idempDeptStepMap typeEmpid columneid propertyeid/idresult columnempName propertyemp_name/resultresult columnage propertyage/resultresult columnsex propertysex/resultresult columnemail propertyemail/result!--select设置分步查询查询某个属性的值的sql的标识namespace.sqlId或mapper接口的全类名.方法名column将sql以及查询结果中的某个字段设置为分步查询的条件--association propertydeptselectmappers.DeptMapper.getEmpDeptByStep columndid/association
/resultMap
!--Emp getEmpByStep(Param(eid) int eid);--
select idgetEmpByStep resultMapempDeptStepMapselect * from t_emp where eid #{eid}
/select再根据员工所对应的部门id查询部门信息
/*** Description: 查询员工信息* CreateTime: 2023/4/2 11:32* Author: HeXin*/
Dept getEmpDeptByStep(Param(did) int did);查询语句
!--Dept getEmpDeptByStep(Param(did) int did);--
select idgetEmpDeptByStep resultTypeDeptselect * from t_dept where did #{did}
/select测试代码
Test
public void testGetEmpAndDept(){SqlSession sqlSession SqlSessionUtils.getSqlSession();EmpMapper mapper sqlSession.getMapper(EmpMapper.class);Emp emp mapper.getEmpAndDept(1);System.out.println(emp);
}测试结果 分布查询的好处是延迟加载(懒加载对某种信息推迟加载这样的技术也就帮助我们实现了 “按需查询” 的机制)而延迟加载默认在MyBatis中不开启的。
如果需要开启延迟加载则需要在核心配置文件的全局设置中设置这两个属性
lazyLoadingEnabled延迟加载的全局开关。当开启时所有关联对象都会延迟加载(默认是关闭的:false)aggressiveLazyLoading当开启时任何方法的调用都会加载该对象的所有属性。 否则每个属性会按需加载(默认是开启的:true)
!--设置MyBatis全局配置--
settings!--开启延迟加载--setting namelazyLoadingEnabled valuetrue/
/settings当开启延迟加载之后可以通过修改association中的fetchType属性的值来手动控制延迟加载的效果
fetchType“lazy”延迟加载fetchType“eager”立即加载
一对多映射处理
通过查询部门及其员工信息为例。
/*** Description:获取部门及其员工信息* CreateTime: 2023/4/2 11:54* Author: HeXin*/
Dept getDeptAndEmp(Param(did) Integer did);通过collection解决
resultMap iddeptAndEmpResultMap typeDeptid propertydid columndid/result propertydeptName columndept_name/!--
collection处理一对多的映射关系
ofType表示该属性所对应的集合中存储数据的类型
--collection propertyemps ofTypeEmpid propertyeid columneid/result propertyempName columnemp_name/result propertyage columnage/result propertysex columnsex/result propertyemail columnemail//collection
/resultMap
!--Dept getDeptAndEmp(Param(id) Integer id);--
select idgetDeptAndEmp resultMapdeptAndEmpResultMapselect * from t_dept left join t_emp on t_dept.did t_emp.did where t_dept.did #{did};
/select分步查询 第一步 先根据did查询部门信息
/*** Description: 分布查询第一步查询部门信息* CreateTime: 2023/4/2 14:52* Author: HeXin*/
Dept getDeptAndEmpByStepOne(Param(did) Integer did);查询语句
resultMap iddeptAndEmpByStepOneResultMap typeDeptid propertydid columndid/result propertydeptName columndept_name/collection propertyempsselectmappers.EmpMapper.getDeptAndEmpByStepTwocolumndidfetchTypelazy/collection
/resultMap
!--Dept getDeptAndEmpByStepOne(Param(did) Integer did);--
select idgetDeptAndEmpByStepOne resultMapdeptAndEmpByStepOneResultMapselect * from t_dept where did #{did};
/select第二步 根据did查询员工信息
/*** Description: 根据did查询员工信息* CreateTime: 2023/4/2 11:54* Author: HeXin*/
ListEmp getDeptAndEmpByStepTwo(Param(did) Integer did);查询语句
!--ListEmp getDeptAndEmpByStepTwo(Param(did) Integer did);--
select idgetDeptAndEmpByStepTwo resultTypeEmpselect * from t_emp where did #{did};
/select测试代码
Test
public void testGetDeptAndEmpByStep(){SqlSession sqlSession SqlSessionUtils.getSqlSession();DeptMapper mapper sqlSession.getMapper(DeptMapper.class);Dept dept mapper.getDeptAndEmpByStepOne(1);System.out.println(部门名称dept.getDeptName());System.out.println(部门iddept.getDid());ListEmp emps dept.getEmps();System.out.println(部门员工);emps.forEach(emp - System.out.println(emp));
}测试结果 从运行的日志中可以发现当代码只查询部门的id和名称的时候只执行了第一句sql语句。而当查询员工信息的时候两条sql语句才同时执行。
动态SQl
Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能它存在的意义是为了解决拼接SQL语句字符串时的痛点问题。
下面以根据多条件查询用户信息为例进行演示学习。
/*** Description: 多条件查询* CreateTime: 2023/4/2 15:23* Author: HeXin*/
ListEmp getEmpByCondition(Emp emp);测试代码
Test
public void testGetEmpByCondition(){SqlSession sqlSession SqlSessionUtils.getSqlSession();DynamicSQLMapper mapper sqlSession.getMapper(DynamicSQLMapper.class);ListEmp emps mapper.getEmpByCondition(new Emp(null,,19,女,xiaohongqq.com));emps.forEach(emp- System.out.println(emp));
}if标签
使用的是xml中的if标签进行sql语句的拼接来解决这个问题。
查询语句
!--ListEmp getEmpByCondition(Emp emp);--
select idgetEmpByCondition resultTypeEmpselect * from t_emp where trueif testempName ! null and empName ! emp_name #{empName}/ifif testage ! null and age !and age #{age}/ifif testage ! null and age !and sex #{sex}/ifif testage ! null and age !and email #{email}/if
/selectwhere后面加上的true(或者恒等式)是为了防止某些条件不成立之后where后面直接接上and导致sql语句出错加上之后也不会影响查询结果 如上面报错信息就是因为empName为空字符串导致where直接与and相连而报错。所以在使用if标签的时候要格外注意这个问题但下面的where标签就能很好的解决这个问题
where标签
where标签会自动识别是否需要生成where关键字并且也会自动屏蔽掉内容前多余的and
!--ListEmp getEmpByCondition(Emp emp);--
select idgetEmpByCondition resultTypeEmpselect * from t_empwhereif testempName ! null and empName ! emp_name #{empName}/ifif testage ! null and age !and age #{age}/ifif testage ! null and age !and sex #{sex}/ifif testage ! null and age !and email #{email}/if/where
/select按照上面的情况执行结果会报错因为empName为空且未加恒等式导致where与and直接相连。
真的是这样吗看一下运行结果 结果没有报错而是直接将员工的信息顺利输出通过观察日志可以发现sql’语句中自动添加了where并且去除了内容前多余的and。
当然这里只是自动生成where如果将查询的所有条件都变成null或者空字符串结果又会怎样呢
观察运行结果和日志发现这次的sql语句中没有where关键字所以sql语句就变成了查询全部员工信息。
注意where标签不能将其中内容后面多余的and或or去除只能将内容前多余的and或or去除
trim标签
trim标签中包含四个属性
prefix将trim标签中内容前面添加指定内容suffix将trim标签中内容后面添加指定内容prefixOverrides将trim标签中内容前面去除指定内容suffixOverrides将trim标签中内容后面去除指定内容
由此可以看出该标签能很好解决where与and的兼容问题
!--ListEmp getEmpByCondition(Emp emp);--
select idgetEmpByCondition resultTypeEmpselect * from t_emptrim prefixwhere prefixOverridesand|orif testempName ! null and empName ! emp_name #{empName}/ifif testage ! null and age !and age #{age}/ifif testage ! null and age !and sex #{sex}/ifif testage ! null and age !and email #{email}/if/trim
/select这里只给出了内容前面加and或or关键字。同理如果在内容后面加上and或or关键字则将prefixOverrides改为suffixOverrides即可。
choose、when、otherwise标签
这里看起来是三个标签实则是一套标签需要组合使用其作用相当于Java中的if…else…
when标签至少有一个而otherwise标签之多只能有一个
!--ListEmp getEmpByCondition(Emp emp);--
select idgetEmpByCondition resultTypeEmpselect * from t_empwherechoosewhen testempName ! null and empName ! emp_name #{empName}/whenwhen testage ! null and age ! age #{age}/whenwhen testsex ! null and sex ! sex #{sex}/whenwhen testempName ! null and empName ! email #{email}/whenotherwisedid 2/otherwise/choose/where
/select从代码中看出其内容中并没有加and因为该标签如果有一个choose条件成立则其他条件就会被忽略所以sql语句中有且最多只有一个成立条件所以不需要加and关键字
foreach标签
foreach标签相当于Java中的for循环下面将以批量删除和批量添加为例进行演示
foreach标签中有五个常用的属性
collection表示的是循环体(集合或数组)item循环体中的个体separator循环个体之间以separator设置的属性值值分开openforeach标签所循环的所有内容的开始符循环从open设置的属性值开始。closeforeach标签所循环的所有内容的结束符循环从close设置的属性值结束。
通过数组实现批量删除
其中批量删除有两种写法一种是id in (删除的id集合或数组)另一种是id id or id…
/*** Description: 通过数组实现批量删除* CreateTime: 2023/4/2 16:33* Author: HeXin*/
int deleteMoreArray(Param(eids) Integer[] eids);删除语句 第一种 !--int deleteMoreArray(Param(eids) Integer[] eids);--
delete iddeleteMoreArraydelete from t_emp where eid inforeach collectioneids itemeid separator, open( close)#{eid}/foreach
/delete第二种 !--int deleteMoreArray(Param(eids) Integer[] eids);--
delete iddeleteMoreArraydelete from t_emp whereforeach collectioneids itemeid separatororeid #{eid}/foreach
/delete通过集合实现批量添加
/*** Description:通过集合实现批量添加* CreateTime: 2023/4/2 16:55* Author: HeXin*/
int insertMoreByList(Param(emps) ListEmp emps);插入语句
!--int insertMoreByList(Param(emps) ListEmp emps);--
insert idinsertMoreByListinsert into t_emp valuesforeach collectionemps itememp separator,(null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},1)/foreach
/insertsql标签
sql标签叫做sql片段可以将常用的sql片段进行记录后续需要使用的时候直接引入使用即可。
接着使用trim标签根据多条件查询用户信息为例继续演示
查询的方法还是一样这里就不赘述了。
查询语句
sql idempColumnseid,emp_name,sex,email,dept/sql
!--ListEmp getEmpByCondition(Emp emp);--
select idgetEmpByCondition resultTypeEmpselect include refidempColumns/ from t_emptrim prefixwhere prefixOverridesand|orif testempName ! null and empName ! emp_name #{empName}/ifif testage ! null and age !and age #{age}/ifif testage ! null and age !and sex #{sex}/ifif testage ! null and age !and email #{email}/if/trim
/select引用时使用到了include标签其中的refid属性表示的是引用sql片段的id MyBatis的缓存
缓存是指将当前查询出来的数据进行一个记录等到下一次再来查询相同的数据时会直接去缓存中去拿取而不再去数据库中拿去。这样的好处时提高了查询的效率当然坏处就是当在服务器中修改了这些数据中的内容如果不及时更新或删除缓存会导致再次查询时出现数据的修改的情况不能及时响应。
MyBatis中的缓存分为一级缓存和二级缓存其中一级缓存是默认开启的。两者的级别不同也就是两者的范围不一样。
一级缓存
一级缓存是SqlSession级别的通过同一个SqlSession查询的数据会被缓存下次查询相同的数据就会从缓存中直接获取不会从数据库重新访问。
下面以根据员工id查询员工信息为例
/*** Description: 根据员工id获取员工信息* CreateTime: 2023/4/2 20:56* Author: HeXin*/
Emp getEmpById(Param(eid) Integer eid);查询语句
!--Emp getEmpById(Param(eid) Integer eid);--
select idgetEmpById resultTypeEmpselect * from t_emp where eid #{eid};
/select下面将通过不同的测试代码来测试其范围 测试代码1 Test
public void testGetEmpById(){SqlSession sqlSession SqlSessionUtils.getSqlSession();//第一次获取员工信息CacheMapper mapper sqlSession.getMapper(CacheMapper.class);Emp emp mapper.getEmpById(2);System.out.println(emp);//第二次获取员工信息Emp emp1 mapper.getEmpById(2);System.out.println(emp1);
}运行结果 从运行结果中可以看到虽然我们用同一个mapper生成了两个相同的员工信息时但是SQL语句只执行了一次。 测试代码2 Test
public void testGetEmpById(){SqlSession sqlSession SqlSessionUtils.getSqlSession();//第一次获取员工信息CacheMapper mapper sqlSession.getMapper(CacheMapper.class);Emp emp mapper.getEmpById(2);System.out.println(emp);//第二次获取员工信息CacheMapper mapper1 sqlSession.getMapper(CacheMapper.class);Emp emp1 mapper1.getEmpById(2);System.out.println(emp1);
}运行结果 当使用不同的mapper生成两个相同的员工信息时但SQL语句仍然只执行了一次。 测试代码3 Test
public void testGetEmpById(){//第一次获取员工信息SqlSession sqlSession SqlSessionUtils.getSqlSession();CacheMapper mapper sqlSession.getMapper(CacheMapper.class);Emp emp mapper.getEmpById(2);System.out.println(emp);//第二次获取员工信息SqlSession sqlSession1 SqlSessionUtils.getSqlSession();CacheMapper mapper1 sqlSession1.getMapper(CacheMapper.class);Emp emp1 mapper1.getEmpById(2);System.out.println(emp1);
}运行结果 当使用不同的SqlSession生成不同的mapper而创建的两个相同的员工数据时SQL语句执行了两次。
通过上面的三个测试样例可以得知一级缓存的范围是在SqlSession内的只要是同一个SqlSession在查询相同数据时都会从缓存中拿取数据。
缓存失效
存在使一级缓存失效的四种情况
不同的SqlSession对应不同的一级缓存同一个SqlSesion但是其查询条件不同同一个SqlSession两次查询期间执行了任意一次增删改操作同一个SqlSession两次查询期间手动清空了缓存(清空缓存方法SqlSession.clearCache())
二级缓存
二级缓存是SqlSessionFactory级别通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存此后若再次执行相同的查询语句结果就会从缓存中获取。二次缓存需要手动开启且二级缓存的范围比一级缓存大。
开启条件
在核心配置文件中设置全局配置属性cacheEnabled“true”(默认是true不需要设置)在映射文件中设置标签cache/二级缓存必须在SqlSession关闭(SqlSession.close()方法)或提交(SqlSession.commit()方法)之后有效查询的数据所转换的实体类类型必须实现序列化的接口 代码演示 准备工作Emp完善实现Serializable的接口在映射文件中加入cache/标签。(开启二级缓存)
测试代码
Test
public void testTwoCache(){InputStream stream null;try {stream Resources.getResourceAsStream(mybatisConfig.xml);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory build new SqlSessionFactoryBuilder().build(stream);//第一次查询SqlSession sqlSession build.openSession(true);CacheMapper mapper sqlSession.getMapper(CacheMapper.class);System.out.println(mapper.getEmpById(1));sqlSession.close();//第二次查询SqlSession sqlSession1 build.openSession(true);CacheMapper mapper1 sqlSession1.getMapper(CacheMapper.class);System.out.println(mapper1.getEmpById(1));sqlSession1.close();}测试结果
要像实现此效果一定要按照开启条件正确开启二级缓存。
缓存失效
两次查询之间执行任意一次的增删改会使一级和二级缓存同时失效。
相关设置
在mapper配置文件中添加的cache标签可以设置一些属性
eviction属性(默认的是 LRU)缓存回收策略 LRU(Least Recently Used)(最近最少使用的移除最长时间不被使用的对象)。 FIFO(First in First out)(先进先出按对象进入缓存的顺序来移除它们)。SOFT – 软引用移除基于垃圾回收器状态和软引用规则的对象。WEAK – 弱引用更积极地移除基于垃圾收集器状态和弱引用规则的对象 。 flushInterval属性刷新间隔单位毫秒 默认情况是不设置也就是没有刷新间隔缓存仅仅调用语句时刷新。size属性引用数目正整数 代表缓存最多可以存储多少个对象太大容易导致内存溢出。readOnly属性只读true/false true只读缓存会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。false读写缓存会返回缓存对象的拷贝通过序列化。这会慢一些但是安全因此默认是 false。
缓存查询的顺序
先查询二级缓存因为二级缓存中可能会有其他程序已经查出来的数据可以直接拿来使用如果二级缓存没有命中再查询一级缓存如果一级缓存也没有命中则查询数据库SqlSession关闭之后一级缓存中的数据会写入到二级缓存中
第三方缓存EHCache
添加依赖
!-- Mybatis EHCache整合包 --
dependencygroupIdorg.mybatis.caches/groupIdartifactIdmybatis-ehcache/artifactIdversion1.2.1/version
/dependency
!-- slf4j日志门面的一个具体实现 --
dependencygroupIdch.qos.logback/groupIdartifactIdlogback-classic/artifactIdversion1.2.3/version
/dependency