文章目录
. Mybatis笔记
1、简介
1.1 什么是mybatis
是一个优秀的持久层框架,支持定制化sql、存储过程和高级映射。
避免了所有jdbc代码和手动设置参数以及获取结果集。
可以使用简单的xml或注解来配置和映射原生类型、接口和Java中的POJO为数据库中的记录。
如何获得mybatis?
- maven仓库
- github
- 中文文档:直接百度搜索获得
1.2 持久化
数据持久化:将程序的数据在持久状态和瞬时状态转换的过程。
内存:断电即失 ,有些对象不能让他丢掉,所以需要持久化。而且内存太贵了。
数据库持久化,io文件持久化。两种方式都可以完成持久化,后者开销大,所以数据库诞生了。
1.3 持久层
Dao层、Service层、Controller层…
- 完成持久化工作的代码块,就叫做持久层
- 层界限十分明显。
1.4 为什么需要MyBatis?
- 方便
- 传统的jdbc代码太复杂.简化.框架.自动化.
- 接触SQL和程序代码的耦合
- 提供xml标签,支持编写动态sql
- 提供对象关系映射标签,支持对象关系组建维护。
- 提供映射标签,支持对象与数据库的orm字段关系映射。
2、第一个Mybatis程序
思路: 搭建环境–>导入Mybatis–>编写代码–>测试
2.1 新建项目
- 新建一个普通的maven项目
- 删除src目录
2.2 创建一个模块
在左面,父工程项目右键,new moudle 还是新建一个普通的maven项目
这样做的好处在于可以直接继承父工程的环境
- 编写mybatis的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"htto://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心-->
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/smbms?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="jiawensili1029"/>
</dataSource>
</environment>
<environment id="tests">
<transactionManager type="JDBC"></transactionManager>
<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>
</configuration>
- 编写mybatis工具类
// sqlSessionFactoryBuilder ---> sqlSessionFactory
// sqlSessionFactory ---> sqlSession
// sqlSession包含了面向数据库执行sql命令所需的所有方法
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 获取sqlSessionFactory。通过建造者模式来获取
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
// 获取sqlSession。通过工厂模式来获取
// SqlSession sqlSession = sqlSessionFactory.openSession();
// return sqlSession;
return sqlSessionFactory.openSession();
}
}
2.3 编写代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ewsMSyb5-1625065882163)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210607145330136.png)]
使用标签来写查询语句,返回集里resultType来定义返回的类型,比如resultType=“com.shen.pojo.User” ,用来返回一个结果
resultMap用来返回一堆结果。
-
先定义pojo类User
-
Dao接口
public interface UserDao { List<User> getUserList(); }
-
接口实现类由原来的UseDaoImpl转变为一个mapper配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <!--namespace=绑定一个对应的Dap/Mapper接口--> <mapper namespace="com.shen.dao.UserDao"> <!--select查询语句--> <select id="getUserList" resultType="com.shen.pojo.User"> select * from smbms_user </select> </mapper>
2.4 使用junit测试
@Test
public void test(){
// 第一步,获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 第二步 获得接口,执行sql
UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.getUserList();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4FLiAoys-1625065882165)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210607224907328.png)]
绑定异常:在mapper注册中心中,接口是未知的
解决方法:
第一步、
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jfRIXk9p-1625065882166)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210607225242309.png)]
只是这样还不够,除非你把UserMapper.xml放到target目录下,否则还是找不到,不生效
但是,我们又不可能每次都手动的把这个东西放进去啊!
出现这个问题的原因,是因为maven的约定大于配置,可能遇到我们写的配置文件,无法被导出或者生效的问题,解决方案如下
第二步、在pom.xml中添加下面代码
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
注意,sqlSession不是线程安全的,因此用完必须关闭(放在finally里)
将上面测试代码改为:
@Test
public void test(){
// 第一步,获得SqlSession对象
SqlSession sqlSession;
try {
sqlSession = MybatisUtils.getSqlSession();
// 第二步 获得接口,执行sql
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}
3、CRUD
3.1 namespace
namespace中的包名要和接口的包名一致!
3.2 select/insert/update/delete标签
选择,查询语句
- id:就是对应的namespace中的方法名
- resultType:sql语句执行的返回值
- parameterType:方法的参数类型
-
编写接口
-
编写对应的mapper中的sql语句
-
测试 注意增删改查需要提交事务
3.3 何时使用map?万能map!
假设我们的实体类,有很多字段,但是我们只修改很少的几个字段,则Dao层接口和xml里方法参数类型将不是User,而是Map。具体的#{}里放入map的key就可以自动识别。
多个参数除了使用map意外,还可以使用注解
3.4 模糊查询怎么写?
不要在java层面写"%"+str +"%"
而是要在sql层面写: “%”#{str}"%" 避免sql注入问题!
例子:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QAINsRNy-1625065882167)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210613234000226.png)]
注意返回类型是User,但事实上接口的返回类型是List
4、配置解析
4.1 核心配置文件
- mybatis-config.xml
- MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息。
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandLers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.2 环境配置(environments)
4.2.1 事务管理器
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3ghK1fcO-1625065882168)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210614231610228.png)]
4.2.2 数据源
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-juFKDaAi-1625065882169)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210614231851368.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UTHrjyUl-1625065882170)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210614231942829.png)]
MyBatis默认的事务管理器是JDBC,连接池:POOLED
4.3 属性(properties)
属性都是可以外部配置且可以动态替换的,既可以在典型的Java属性文件中配置,也可以通过properties元素的子元素来传递。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VD2hzhY3-1625065882170)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210614232635346.png)]
注意,xml里,约定了标签的先后顺序,properties必须放在很前面的位置。
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/smbms?useSSL=TRUE&useUnicode=TRUE&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=jiawensili1029
注意,在properties里,driver和url同上面略有区别,true必须大写,不能用& amp;来代表&,driver多个cj,因为不带的已经被淘汰。前面的jdbc.都可以去掉
小结
- 可以直接引入外部文件
- 可以在其中增加一些属性配置
- 如果两个文件有同一个字段,优先使用外部配置文件的
4.4 别名
在xml里
<select id="getUserByUserCode" parameterType="String" resultType="com.shen.pojo.User">
select * from smbms_user where userCode = #{userCode}
</select>
com.shen.pojo.User太长了,是冗余的。因此使用别名。
- 方法1.在config.xml里设置别名
<!--可以给实体类型起别名-->
<typeAliases>
<typeAlias type="com.shen.pojo.User" alias="user"></typeAlias>
</typeAliases>
-
方法2.指定一个包名,MyBatis会在包名下面搜索需要的JavaBean
扫描实体类的包,它的默认别名就为这个类的类名的首字母小写(使用的时候写成大写也可以)。
<typeAliases>
<!--<typeAlias type="com.shen.pojo.User" alias="user"></typeAlias>-->
<package name="com.shen.pojo"/>
</typeAliases>
第一种方法可以自定义别名,第二种则需要注解自定义别名。实体类较少建议使用第一种,实体类较多建议使用第二种。第二种方式中,可以用注解来起别名。
@Alias("hello")
public class User {
// 实体类
...
}
<select id="getUserLike" parameterType="String" resultType="hello">
select * from smbms_user where userName like "%"#{username}"%"
</select>
Java类型中有一些内建的别名。
比如==_int代表int;int代表Integer==
4.5 设置
驼峰命名与数据库命名映射转换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c95Lsfos-1625065882171)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210619213305302.png)]
4.6 其他配置
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins插件(maven搜mybatis)
- mybatis-plus
- mybatis-genrator-core
- 通用mapper
4.7 映射器(mappers)
MapperRegistry:注册绑定我们的Mapper文件
<mappers>
<!--1.使用相对路径资源引用-->
<mapper resource="com/shen/dao/UserMapper.xml"></mapper>
<!--2.使用url完全限定资源定位符,不建议使用-->
<!--3.使用映射器接口的实现类的完全限定类名-->
<mapper class="com.shen.dao.UserMapper"></mapper>
<!--4.将包内的映射器接口实现全部注册为映射器-->
<package name="com.shen.dao"/>
</mappers>
使用方式3和方式4时会遇到的坑:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4bhQCBKp-1625065882172)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210619214923455.png)]
- 1.接口和他的Mapper配置文件必须在同一个包下.即UserMapper和UserMapper.xml必须在同一个包下。
- 2.接口和他的Mapper配置文件必须同名!
4.8 生命周期和作用域
这两个东西很重要,因此错误的使用会导致非常严重的并发问题
SqlSessionFactoryBuilder
- 一旦创建了SqlSessionFactory,就不需要需要SqlSessionFactoryBuilder了
- 所以它是局部变量
SqlSessionFactory
- 可以类比为数据库连接池
- 一旦创建就在运行期间一直存在,没有任何理由丢弃它或者重新创建它,多次重建会让代码bad smell(产生坏味道)
- 因此SqlSessionFactory最佳作用域是应用作用域
- 最简单的就是使用单例模式或者静态单例模式
SqlSession
- 它是链接到连接池的一个请求
- SqlSession的实例不是线程安全的,因此不能被共享,所以最佳作用域是请求(每次收到HTTP请求以后就创建一个SqlSession,用完就关闭)或方法作用域
- 用完之后赶紧关闭,否则浪费资源。
- 一个SqlSession可以连接多个Mapper,这里每一个Mapper,就代表一个具体的业务
5、解决属性名和字段名不一致问题
public class User {
// 实体类
private Integer id;
private String userCodeCodeHa; // 改这里
}
经过测试发现,如果但改这里和所有的userCode参数变为userCodeCodeHa,还是可以从数据库里查到。但是如果把set方法和get方法也改了。比如把setUserCode改为setUserCodeCodeHa以后,就在数据库里找不到这个字段名对应的数据了(输出null)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZM3JKOhe-1625065882172)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210619223025121.png)]
因此可以推测,MyBatis是通过set和get方法的名字,来一一对应java中pojo的实体类的属性名与数据库中字段名的。
-
方法一:在sql语句里起别名
<select id="getUserByUserCode" parameterType="String" resultType="hello"> select userpassword,usercode as usercodecodeha from smbms_user where userCode = #{userCode} </select>
其实从这个方法,可以看出,mybatis其实是执行sql以后,把结果的那个列名,拼接上“set”,构成了SetUserCodeCodeHa(不区分大小写),然后在java对应的pojo实体类里,找到对应的这个方法,然后把列名对应的属性作为参数,传入这个set方法里。
-
方法二:resultMap 结果集映射
-
首先,给select标签里的resultMap随便起个名字,这里叫UserMap吧
<select id="getUserByUserCode" parameterType="String" resultMap="UserMap"> select * from user_test where usercode = #{userCode} </select>
-
然后,在resultMap标签,做一个映射。注意这里严格区分大小写
<!--column数据库中的字段,property实体类中的属性 严格区分大小写--> <resultMap id="UserMap" type="hello"> <result column="usercode" property="userCodeCodeHa"></result> <!--对于没有别名的这两句,其实可以不写--> <result column="userpassword" property="userPassword"></result> <result column="username" property="userName"></result> </resultMap>
-
6、日志
6.1 日志工厂
如果一个数据库操作出现异常,可以通过日志工厂来排错。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PEmrUfI0-1625065882173)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210620172235045.png)]
主要掌握LOG4J和STDOUT_LOGGING
在Mybatis中具体使用哪个日志实现,在设置中实现。
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cq2cb4Ll-1625065882173)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210620172712062.png)]
STDOUT_LOGGING是标准的日志工厂,可以直接用
其他的,比如LOG4J就需要导包以后才能用,否则找不到类
6.2 LOG4J
log4j的apache的一个开源项目。可以控制日志信息输送的目的地是控制台、文件还是GUI组件。可以控制每一条日志的输出格式、定义每一条日志信息的级别。可以通过一个配置文件来灵活地进行配置,不需要修改应用的代码。
-
先导入log4j的包
<dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
-
log4j.properties
#将等级为DEBUG的日志信息输出到console和file两个目的地。console和file的定义在下面的代码 log4j.rootLogger=debug,file,console #输出到文件相关设置 log4j.appender.file.Append = true log4j.appender.file.MaxFileSize = 10MB log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File = ./log/com.shen 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.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.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
-
配置log4j为日志的实现
-
使用
6.3 日志级别
info、error、debug
7、分页
7.1 limit分页
- 减少数据的处理量
// 分页
List<User> getUserByLimit(Map<String,Integer> map);
<select id="getUserByLimit" parameterType="map" resultType="user">
select * from smbms_user limit #{startIndex},#{pageSize}
</select>
7.2 RowBounds分页
<select id="getUserByRowBounds" resultMap="UserMap">
select * from smbms_user
</select>
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 其实RowBounds本质也是在调用limit
RowBounds rowBounds = new RowBounds(2,5);
// 通过Java层面代码实现分页
List<User> list = sqlSession.selectList("com.shen.dao.UserMapper.getUserByRowBounds",null,rowBounds);
for(User user : list){
System.out.println(user);
}
}
7.3 在maven里用一些其他的分页插件,如pageHelper
8、使用注解开发
面向接口编程的根本原因:解耦、提高复用。
不需要mapper.xml,对于简单的sql语句,可以直接在接口中使用注解。
public interface UserMapper {
@Select("select * from smbms_user")
List<User> getUsers();
}
本质:反射机制实现
底层:动态代理—代理模式!
9、Lombok
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0lwUtLMC-1625065882174)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210621180320371.png)]
使用maven仓库引入lombok的jar包。
只需要一个注解 Data,就生成了:
无参构造、set、get、toString、hashcode、equals
@NoArgsConstructor
@AllArgsConstructor
无参构造和有参构造的注解
10、多对一处理
10.1 按照查询嵌套处理
例:smbms_user的userRole不是Integer类型,而直接使用Role类型的实体。
Pojo:Role
@Data
public class Role {
private Integer roleCode;
private String roleName;
}
Pojo:User
@Data
public class User {
// 实体类
private Integer id;
private String userCode; // # 解决属性名和字段名不一致问题
private String userName;
private Role userRole;
}
UserMapper.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" >
<!--namespace=绑定一个对应的Dap/Mapper接口-->
<mapper namespace="com.shen.dao.UserMapper">
<!--这里的userAndRole本质上还是一个User类型的,但因为涉及到复杂属性,不得不另起一个map来描述清楚-->
<resultMap id="userAndRole" type="user">
<result property="userCode" column="userCode"></result>
<result property="userName" column="userName"></result>
<!--复杂的属性需要单独处理,对象:association 集合:collection-->
<association property="userRole" column="userRole" javaType="Role" select="getRoleByRoleCode"></association>
</resultMap>
<select id="getUsersAndRoleName" resultMap="userAndRole">
select * from smbms_user
</select>
<select id="getRoleByRoleCode" resultType="role">
select * from smbms_role where roleCode = #{roleCode}
</select>
</mapper>
注意,其中
<select id="getRoleByRoleCode" resultType="role">
select * from smbms_role where roleCode = #{roleCode}
</select>
这个查询,不是放在RoleMapper.xml而是放在UserMapper.xml
因为getUsersAndRoleName这个接口的实现,要使用getRoleByRoleCode(这个在RoleMapper接口里定义,在UserMapper.xml里实现)
<association property="userRole" column="userRole" javaType="Role" select="getRoleByRoleCode"></association>
本质其实就是property的userRole对应User类里的Role userRole的实例对象。
column的userRole对应数据库表里smbms_user的userRole(BigInteger类型)
javaType相当于是要使用column作为参数,去select后面那个语句以后的返回类型,正好就是Role类型嘛!因为getRoleByRoleCode返回的是role类型。
以上称之为按照查询嵌套处理
10.2 按照结果嵌套处理
例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R8BT1I8u-1625065882174)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210621230423305.png)]
返回学生姓名和学生对应的老师的姓名
回顾mysql嵌套查询方法:子查询、联表查询
其实就是对应上面的按照查询嵌套和按照结果嵌套
11、一对多处理
比如:一个老师拥有多个学生!对于老师而言,就是一对多。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FSh8Tgh-1625065882175)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210621230804334.png)]
学生类则是单独的和数据库一一对应的pojo
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8qzTr234-1625065882175)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210621232357846.png)]
一对多使用集合Collection 集合中的元素类型不用javaType,而是用ofType表示
12、动态sql
动态sql就是指根据不同的条件生成不同的sql语句
if
<select ....>
<if test=" title!=null ">
AND title like #{title}
</if>
</select>
choose(when ,otherwise)
类似于java中的swich语句
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hwpI7ufU-1625065882176)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210623230045371.png)]
两个when条件都满足,但只走了第一个.相当于switch case breake了
trim(where,set)
where元素只会在至少有一个子元素的条件返回SQL子句的情况下才会插入where,若语句开头为AND或者OR,where元素会自动将它们去除.
where和set都是trim的子元素.使用trim可以自己定制类似where和set的标签,并且定义前缀后缀/前缀覆盖后会覆盖.
前缀/后缀:比如where,如果没有任何子元素,就自动把where去掉
前缀覆盖:比如and和or,如果是第一个子元素,就会自动去掉and和or,第二个开始的子元素会保留and和or
后缀覆盖:比如set的逗号,如果是最后一个set的子元素,会把逗号去掉,不然sql里多一个逗号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LtH3nzze-1625065882176)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210623225935781.png)]
foreach
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xbOreIug-1625065882176)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210623231256430.png)]
item就是从collection里遍历的每一项
例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dCbEWoqk-1625065882177)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210623231413483.png)]
因为where标签很"智能",所以不需要写1=1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6k44Wt1h-1625065882177)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210623231810060.png)]
ids使用map传入
sql标签与include标签(sql片段)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GInpd2Kl-1625065882178)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210623230700242.png)]
可以用来存取一个sql片段,避免重写一遍.
注意事项:
- 最好基于单表来定义sql片段!
- 不要存在where标签!
13、缓存
一级缓存
sqlSession级别的缓存。一级缓存默认开启,且无法关闭,只在一次sqlSession中有效
比如在一次sqlSession会话中,查询两次同一条语句,但sql其实只走了一次。
一级缓存就是一个Map
实现方式:LRU(最长时间不用的就被顶替)、FIFO(最先使用过的被顶替)
缓存失效的情况:
-
查询不同的东西
-
增删改操作可能改变原来的数据,所以必定会刷新缓存
-
查询不同的Mapper.xml
-
手动清理缓存
sqlSession.clearCache();
二级缓存
namespace级别的缓存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Btx4QK2r-1625065882179)(/Users/shenhangran/Desktop/学习笔记/我的/Mybatis笔记.assets/image-20210623234039925.png)]
步骤:
- 在config的settings里开启缓存
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 在当前mapper中使用
<!--在当前mapper中使用二级缓存 每隔60秒刷新-->
<cache eviction="FIFO" flushInterval="60000"
size="512" readOnly="true"/>
刷新时间单位为毫秒。
记得在具体的一个sql语句的地方开启cache
<select id="getUsersAndRoleName" resultMap="userAndRole" useCache="true">
select * from smbms_user
</select>
小结
- 二级缓存有弊端,需要pojo实体类实现Serializable接口保证不报错(readonly设置为false时)
- 只要开了二级缓存,在同一个mapper下就有效!
- 所有的数据都会放在一级缓存中,只有会话提交或者关闭的时候,才会提交到二级缓存。即.只有一级缓存崩了以后,二级缓存才生效
readOnly设置为false时,下面的代码会输出false,否则会输出true。原因是因为事先了Serializable接口,深拷贝,哈希变了,但因为开了二级缓存,所以还是只走了一遍sql!
public void test1(){
SqlSession sqlSession1 = MybatisUtils.getSqlSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
List<User> users1 = mapper1.getUsersAndRoleName();
sqlSession1.close(); // 得先关闭。因为只有一级缓存崩了以后二级缓存才生效
System.out.println("------");
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
List<User> users2 = mapper2.getUsersAndRoleName();
System.out.println(users1==users2);
sqlSession2.close();
}
总结:
对于用户的命令而言,先从二级缓存里看有没有,没有的话看一级缓存里有没有,还没有的话才去数据库里查找!
ehcache
maven导入ehcache可以自定义缓存,也可以用现成的。
type属性指定的类必须实现org.mybatis.cache.Cache接口且提供一个String id