- 1. 简介
- 1.1 什么是Mybatis?
- 1.2 如何获得Mybatis?
- 2. 创建第一个Mybatis程序
- 2.1 搭建环境
- 2.2 创建一个模块
- 2.3 编写代码
- 2.4 测试
- 2.5 注意点
- 3. CRUD
- 3.1 namespace
- 3.2 select
- 3.3 insert
- 3.4 update
- 3.5 delete
- 3.6 万能的Map
- 总结
- 3.7 模糊查询
- 4. 配置分析
- 4.1 核心配置文件
- 4.2 环境配置(environments)
- 4.3 属性(properties)
- 4.4 类型别名(typeAliases)
- 4.5 映射器(mappers)
- 5. 生命周期
- 6. 结果集映射(resultMap)
- 7. 日志
- 7.1 标准的日志工厂实现(STDOUT_LOGGING)
- 7.2 Log4j
- 8. 分页
- 9. 注解
- 9.1 注解开发
- 9.2 注解实现CRUD
- 10. Lombok
- 11. 多对一
- 11.1 测试环境搭建
- 11.2 多对一的处理
- 12. 一对多
- 13. 动态SQL
- 13.1 if
- 13.2 where
- 13.3 choose、when、otherwise
- 13.4 set
- 13.5 SQL片段
- 13.5 foreach
- 14. 缓存
- 14.1 简介
- 14.2 Mybatis缓存
- 一级缓存
- 二级缓存
- 14.3 缓存原理
Mybatis是一款优秀的持久层框架
它支持定制化SQL、存储过程以及高级映射
Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
Mybatis可以使用简单的XML或注解来配置和映射原生类型、接口和Java的pojo为数据库中的记录
maven仓库
org.mybatis mybatis 3.5.2
Github: https://github.com/mybatis/mybatis-3/releases
MyBatis文档网址:https://mybatis.org/mybatis-3/zh/index.html
1.新建一个普通的maven项目
2.删除src目录
3.加入maven依赖
mysql
mysql-connector-java
5.1.47
org.mybatis
mybatis
3.5.2
junit
junit
4.12
2.2 创建一个模块
1.编写mybatis的核心配置文件
2.编写mybatis工具类
// SqlSessionFactory -->sqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 使用Mybatis第一步:
// 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用Mybatis第二步:
// 从 SqlSessionFactory 中获取 SqlSession
// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.3 编写代码
1.编写实体类(一个实体类对应一个表)并实现Serializable接口。
package com.liu.pojo;
//实体类
public class User implements Serializable{
public int id;
public String name;
public String pwd;
public User() {
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getPwd() {
return pwd;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + ''' +
", pwd='" + pwd + ''' +
'}';
}
}
2.编写Dao接口
public interface UserDao {
List getUserList();
}
3.编写接口对应的Mapper.xml文件(以前是一个UserDaoImpl对应一个UserDao,现在是一个Mapper.xml文件对应一个UserDao)
2.4 测试
构造尽量保持规范,在test下建立com.liu.dao,并编写测试类UserDaoTest
public class UserDaoTest {
@Test
public void test() {
// 第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一;getMapper
UserDao userDao = sqlSession.getMapper(UserDao.class);
List userlist = userDao.getUserList();
for (User user : userlist) {
System.out.println(user);
}
// 关闭SqlSession
sqlSession.close();
}
}
2.5 注意点
1.每一个Mapper.xml都需要在Mybatis核心配置文件中注册!
2.maven由于他的约定大于配置,可能会遇到我们写的配置文件(.xml或者.properties)无法被导出或者生效的问题,解决方案为在pom文件中贴如下代码:
src/main/java ***.xml false src/main/resources ***.xml false
3.空指针问题:可能是重复定义了某变量所致
3. CRUD 3.1 namespacenamepspace中的包名要和Dao/Mapper接口的包名一致!3.2 select
选择,查询语句;
- id:就是对应的namespace中绑定的Mapper接口中的方法名
- resultType:sql语句执行后的返回值
- parameterType:参数类型
- 通过 #{} 来取传递的参数
编写接口
//根据ID查询用户
User getUserById(int id);
编写对应的mapper中的sql语句
测试
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user=mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
3.3 insert
编写接口
//插入
int addUser(User user);
编写对应的mapper中的sql语句
insert into mybatis.user(id, name, pwd) values (#{id},#{name},#{pwd})
测试
//增删改查需要提交事务
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User(4,"哈哈","123333"));
if(res>0)
System.out.println("插入成功!");
//提交事务
sqlSession.commit();
sqlSession.close();
}
3.4 update
编写接口
//修改
int updateUser(User user);
编写对应的mapper中的sql语句
update mybatis.user set name = #{name}, pwd = #{pwd} where id = #{id}
测试
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res=mapper.updateUser(new User(4,"呵呵","111111"));
if(res>0)
System.out.println("修改成功!");
//提交事务
sqlSession.commit();
sqlSession.close();
}
3.5 delete
编写接口
//删除
int deleteUser(int id);
编写对应的mapper中的sql语句
delete from mybatis.user where id = #{id}
测试
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res=mapper.deleteUser(4);
if(res>0)
System.out.println("删除成功");
// 提交事务
sqlSession.commit();
sqlSession.close();
}
注意点:增删改需要提交事务!!
另一种方式:不需要commit!
将openSession中的参数设为true即可。
假设我们的实体类,或者数据库中的表,字段或者参数过多,则应当考虑使用map。与直接传递对象的区别就在于使用map不一定要获取到所有的属性,而当使用User时,必须将所有的属性包揽在内
1.接口中参数声明为Map形式
//修改:万能的Map
int updateUser2(Map map);
2.Mapper中:参数类型是小写的map, 里面的值是map的key
update mybatis.user set name= #{name} where id=#{id}
3.测试代码
@Test
public void updateUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map=new HashMap();
map.put("id",1);
map.put("name","刘梦豪"); // 最大的区别就在于使用map不需要获取到所有的属性,而当使用User时,必须将所有的属性包揽在内
mapper.updateUser2(map);
sqlSession.commit();
sqlSession.close();
}
总结
Map传递参数,直接在sql中取出key即可!【parameterType=“map”】
#{key} 即可取到
对象传递参数,直接在sql中取对象的属性即可!【parameterType=“Object”】
#{对象包含的属性名}即可取到
对于方法中只有一个基本类型参数的情况下,可以直接在sql中取到!
#{参数名}
对于方法中多个参数:用Map,或者注解@Param("") 后续会讲到
1.编写接口
// 模糊查询
List getUserLike(String value);
2.编写对应的mapper中的sql语句并测试
方式一:
对应的测试代码为:
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List userList = mapper.getUserLike("%李%");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
方式二:
对应的测试代码为:
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List userList = mapper.getUserLike("李");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
4. 配置分析
4.1 核心配置文件
一般命名为:mybatis-config.xml
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)4.2 环境配置(environments)
MyBatis 可配置成适应多种环境
但是尽管可以配置多种环境,但每个 SqlSessionFactory 实例只能选择一种环境!
我们可以通过properties来实现引用配置文件,这些属性都是可以外部配置且可动态替换的。既可以在典型的Java属性文件中配置,也可以通过properties元素的子元素来传递(db.properties)
编写一个配置文件
在核心配置文件中可引入外部配置文件
如果里面和外面的配置冲突了,优先使用外面的配置
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
第一种写法:
...
第二种写法:扫描一个包,每个实体类的默认别名就为这个类的类名,首字母一般小写!(大写也行)
第三种写法:注解方式(在第二种写法的基础上,如果要修改名时则使用,如果一三写法同时使用,第一种生效)
直接在类的上面使用注解@Alias(“自定义名称”)
@Alias("hello")
public class User {
...
}
4.5 映射器(mappers)
每一个mapper都需要注册,注册mapper有三种方式
方式一:使用相对于类路径的资源引用
...
方式二:使用映射器接口实现类的完全限定类名
...
方式三:使用扫描包进行注入
...
后两种方式的注意点:
- 接口和他的Mapper配置文件必须同名!
- 接口和他的Mapper配置文件必须在同一个包下!
SqlSessionFactoryBuilder:
- 这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory:
- 说白了就是可以想象为:数据库连接池
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次创建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。
- 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession:
- 是连接到连接池的一个请求
- 每个线程都应该有它自己的 SqlSession 实例。
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 用完之后需要赶紧关闭,否则资源被占用!
这里面的每一个Mapper,就代表一个具体的业务!
当表的字段名与实体类字段名不对应的时候,要进行映射。(在mapper.xml文件中)
实体类对应属性名property,数据库字段名column
sql语句中的resultType改为resultMap
7. 日志
如果一个数据库操作出现了异常,我们需要排错。日志就是最好的助手。
一般采用日志工厂来解决。掌握(STDOUT_LOGGING和LOG4J)
在Mybatis中具体使用哪一日志,需要在设置中设定!
7.2 Log4j
什么是Log4j?
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
- 我们可以控制每一条日志的输出格式
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1.先导入log4j依赖
log4j log4j 1.2.17
2.在resources下增加文件log4j.properties(可以写成其他的)
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置;第二行表示日志会被保存在的位置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/liu.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3.在mybatis核心配置文件中配置log4j为日志实现
4.简单使用
-
在需要使用log4j的类中,导入包 import org.apache.log4j.Logger;
-
获取日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
-
举例
public class UserMapperTest { static Logger logger = Logger.getLogger(UserMapperTest.class); @Test public void log4jTest(){ logger.info("info:进入了testLog4j"); logger.debug("debug:进入了testLog4j"); logger.error("error:进入了testLog4j"); } }
为什么要分页?
- 减少数据的处理量
limit语法:
对于一个参数的情况就是显示出前n行数据
select * from product limit n; // 展示[0,n]
limit有两个参数,第一个参数表示从第几行数据开始查(startIndex),第二个参数表示每页的大小(pageSize)
select * from product limit 3,2; //“limit 3,2”表示从第四行数据开始,取两条数据。
1.定义接口
//分页
List getUserByLimit(Map map);
2.在mapper.xml文件实现
select * from mybatis.user limit #{startIndex},#{pageSize};
3.测试
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap map = new HashMap();
map.put("startIndex",1);
map.put("pageSize",2);
List userlist = mapper.getUserByLimit(map);
for (User user : userlist) {
System.out.println(user);
}
sqlSession.close();
}
还有一种分页方式不需要写sql代码,通过RowBounds实现
9. 注解 9.1 注解开发1.注解形式:(在接口文件里面方法上直接添加注解)
public interface UserMapper {
@Select("select * from user")
List getUsers();
}
2.在mybatis-config.xml文件中添加类路径映射,绑定接口
本质:利用Java反射
底层:代理
注意: 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好还是用mapper. xml来映射语句。
我们可以在工具类创建的时候设置自动提交事务
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
有关@Param注解:
1.如果方法中是一个参数,可以忽略注解,但是建议写上
2.但是如果是多个参数,必须要使用 @Param 注解。引用类型不用加
3.@Param("")注解的意思是,上面的sql语句中用到的变量是从这个注解里取到的。以注解中的名称为准(重要)
编写接口,增加注解
@Select("select * from user")
List getUsers();
// 方法存在多个参数,所有的参数前面必须加上@Param(“”)注解
@Select("select * from user where id = #{id} and name = #{name}")
User getUserByIDName(@Param("id") int id, @Param("name") String name);
@Insert("insert into user(id,name,pwd) values (#{id}, #{name}, #{password})")
int addUser(User user);
@Update("update user set name=#{name}, pwd=#{password} where id = #{id}")
int updateUser(User user);
// 以注解中的名称为准
@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
测试类
public class UserMapperTest {
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 查询全部用户
// List users = mapper.getUsers();
// for (User user : users) {
// System.out.println(user);
// }
//增加
// mapper.addUser(new User(5,"李五","123456"));
//更新
// mapper.updateUser(new User(5,"赵六","111222"));
//根据id和name查询
// User user = mapper.getUserByIDName(1, "刘梦豪");
// System.out.println(user);
//删除
// mapper.deleteUser(5);
sqlSession.close();
}
}
有关#{} 和 ${}:
#{}是预编译的,可以有效防止sql注入。
${}不安全,能用#{}就用#{}。
除了日志功能,还有给实体类使用的@Data注解,可以省去写get、set方法。只需要在实体类前加一个@Data注解,方法如下:
1.先安装Lombok插件
2.在pom文件中添加依赖
org.projectlombok
lombok
1.18.10
3.实体类前加注解@Data
注意:
@Data除了get、set方法外,还提供了equals、canEqual、hasCode、toString方法,以及无参的构造方法。
可以使用 @AllArgsConstructor 注解添加有参的构造方法,但此时无参的会消失。可以再添加 @NoArgsConstructor 注解添加无参的构造方法。
@Data @AllArgsConstructor @NoArgsConstructor11. 多对一 11.1 测试环境搭建
1.导入Lombok
2.新建实体类Teacher,Student
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
// 学生需要关联一个老师
private Teacher teacher;
}
3.建立Mapper接口
4.建立Mapper.xml文件
可以放在resources中,但目录需相同仍为com.liu.dao,这样在target中仍会将接口与mapper.xml打包在一起。
对于创建的mapper.xml文件,不需每次都去网上copy,只需将mybatis-config.xml中的内容复制过来。修改下列三处再配置namespace即可。
5.在核心配置文件中绑定注册我们的Mapper接口或者文件
6.测试查询
@Test
public void getTeacherByID()
{
SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacherByID(1);
System.out.println(teacher);
sqlSession.close();
}
11.2 多对一的处理
比如多个学生对应一个老师
方式一:子查询
select * from student where id =(select * from teacher…)
select * from student
方式二:联表查询(按结果嵌套查询)
直接根据sql语句得到结果,然后去嵌套映射结果
测试代码
@Test
public void testStudent(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List studentList = mapper.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
@Test
public void testStudent2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List studentList = mapper.getStudent2();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
查询结果
一个老师包含多个学生
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
private int tid;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
// 一个老师拥有多个学生
private List students;
}
方式一:按照结果嵌套查询
方式二:采用子查询的方式
测试代码
@Test
public void getTeacher(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
//关闭SqlSession
sqlSession.close();
}
@Test
public void getTeacher2(){
//第一步:获得SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
//方式一:getMapper
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher2(1);
System.out.println(teacher);
//关闭SqlSession
sqlSession.close();
}
小结:
1.关联- association【多对一】
2.集合- collection 【一对多】
3. javaType & ofType
1. JavaType 用来指定实体类中属性的类型.
2. ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类 型!
按照结果嵌套查询: 直接写出完整的连接查询sql语句,获取到想要的结果。然后再与实体类中的属性一一映射起来
按照子查询的方式: 先写出查询一个表的sql语句,然后在where条件中再去查询另一个表(即子查询)
什么是动态SQL:
- 动态SQL就是根据不同的条件生成不同的SQL语句。
- 本质还是sql语句,只是我们可以在sql层面,去执行一个逻辑代码。
- 动态SQL就是在拼接SQL语句,我们只要保证SQL的正确性,按照SQL的格式,去排列组合就可以了。
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
select * from mybatis.user where 1=1 and id = #{id}
where 1=1 不太符合规范,因此引入where元素
13.2 wherewhere元素只会在至少有一个子元素返回sql子句的情况下才去插入 “WHERe” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
13.3 choose、when、otherwiseselect * from mybatis.user id = #{id} and name =#{name}
类似于switch语句,若前面when满足,就不会再往后判断,当when中全不满足,则默认选otherwise
select * from mybatis.user id = #{id} and name = #{name} and pwd = #{pwd}
测试代码
@Test
public void getUser2()
{
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap map = new HashMap();
// map.put("id",1); //当这行不注释时,会选他,反之就选下面那个
map.put("pwd","444444");
User user = mapper.getUser2(map);
System.out.println(user);
sqlSession.close();
}
13.4 set
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
update mybatis.user where id = #{id} name = #{name}, pwd = #{pwd}
测试代码
@Test
public void updateUser()
{
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap map = new HashMap();
map.put("id",1);
map.put("name","刘刘");
map.put("pwd","444455");
int res = mapper.updateUser(map);
System.out.println(res);
sqlSession.close();
}
13.5 SQL片段
有的时候,我们可能会将一些公共的部分抽取出来,方便复用!
1.使用SQL标签抽取公共的部分
2.在需要使用的地方使用include标签引用即可
id = #{id} and name =#{name} select * from mybatis.user
测试代码
@Test
public void getUser()
{
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUser(1,"刘刘");
System.out.println(user);
sqlSession.close();
}
注意:
- 最好基于单表来定义SQL片段
- 不要存在where标签
适用于对于一个属性有多个取值的sql语句
举例:比如查询id为1 2 3 的用户(用or)
select * from user where (id=1 or id=2 or id=3)
select * from mybatis.user id = #{id}
提示: 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为collection参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。open是初始拼接字符串,close是末尾拼接字符串。separator是遍历对象间的分隔符
测试代码
@Test
public void getUser3()
{
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap map = new HashMap();
ArrayList ids = new ArrayList();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids); // 将ids列表作为参数
List userList= mapper.getUser3(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
14. 缓存
14.1 简介
1.什么是缓存?
- 存在内存中的临时数据
- 将用户经常查询的数据放在缓存中,用户去查询数据就不用从磁盘上查询,从缓存中查询,从而提高查询效率,解决高并发系统的性能问题
2.为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率
3.什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据
Mybatis 使用到了两种缓存:本地缓存(local cache)(一级缓存)和二级缓存(second level cache)。
一级缓存一级缓存也叫本地缓存:SqlSession
- 与数据库同一次会话期间查询到的数据会放在本地缓存中
- 以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
测试步骤:
- 开启日志!
- 测试在一个Session中查询两次相同记录
- 查看日志输出
测试代码
@Test
public void getUser()
{
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUser(1,"刘刘");
System.out.println(user);
System.out.println("======================================");
user = mapper.getUser(1,"刘刘");
System.out.println(user);
sqlSession.close();
}
如下图所示,缓存开启,查询两次相同的东西,只执行一次sql
缓存失效的情况:
- 查询不同的东西
- 增删改操作,可能会改变原来的数据,所以必定会刷新缓存
- 查询不同的Mapper.xml
- 手动清理缓存 sqlSession.clearCache();
一级缓存是默认开启的,只在一次SqlSession中有效!
二级缓存-
二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
-
基于namespace级别的缓存,一个名称空间,对应一个二级缓存
-
工作机制:
- 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
- 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话提交或关闭的时候,一级缓存中的数据被保存到二级缓存中;
- 新的会话查询信息,就可以从二级缓存中获取内容;
- 不同的mapper查出的数据会放在自己对应的缓存(map)中;(也就是说只要开启了二级缓存,在同一个Mapper下就有效)
步骤:
1.开启全局缓存(mybatis-config.xml中setting里)
2.在SQL映射文件(xxxmapper.xml)中添加一行
也可以指定的详细一些:
总结:
- 只要开启了二级缓存,在同一个Mapper下就有效
- 所有的数据都会先放在一级缓存中
- 只有当会话提交,或者关闭的时候,才会提交到二级缓存中



