}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return “Employee{” +
“id=” + id +
“, lastName='” + lastName + ‘’’ +
“, email='” + email + ‘’’ +
“, gender='” + gender + ‘’’ +
‘}’;
}
}
下面这个就是sql映射文件
-
namespace就是命名空间;指定为接口的全类名
-
id就是唯一标识
-
resultType就是返回值类型,比如我们想返回的是员工的信息,那我们就返回Employee的全类名
-
#{id}:从传递过来的参数中取出id值
-
id指定接口中的方法,这样不仅文件和接口绑定,select标签也和方法进行了绑定
以上几点都是重点!!!画黑板了,记到本子上!
映射文件
<?xml version="1.0" encoding="UTF-8" ?>select * from tbl_employee
where id = #{id}
将我们写好的sql映射文件一定要注册到全局配置文件中
测试效果
@Test
public void test01() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(employee);
} finally {
openSession.close();
}
}
结果如下:
Employee{id=1, lastName=‘tom’, email=‘tom@caq.com’, gender=‘0’}
面向接口编程
什么是面向接口编程?
面向接口编程是先把客户的业务逻辑线提取出来,作为接口,业务具体实现通过该接口的实现类来完成。当客户需求变化时,只需编写该业务逻辑的新的实现类,通过更改配置文件中该接口的实现类就可以完成需求,不需要改写现有代码,减少对系统的影响。(这个概念的理解很重要,先记着后面实践的时候会越来越清晰!)
这里我结合后面springboot的一些方式,释放了mybatis-config.xml文件,通过yml文件中的指定来替换。数据库映射mapper.xml文件通过在接口上写@Mapper注解和方法上写注解的方式实现
如下:
yml文件
mybatis:
通过yml配置文件的方式解放了mybatis-config.xml文件
configuration:
map-underscore-to-camel-case: true #开启驼峰命名
Mapper接口
package com.caq.admin.mapper;
import com.caq.admin.bean.Account;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
//通过写@Mapper注解的方式释放了写mapper.xml映射文件的方式
@Mapper
public interface AccountMapper {
@Select(“select * from account where id = #{id}”)
Account test(Long id);
}
service层
package com.caq.admin.service;import com.caq.admin.bean.Account;import com.caq.admin.mapper.AccountMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class AccountService { @Autowired AccountMapper accountMapper; public Account getAcc(Long id){ return accountMapper.test(id); }}
controller层
@Controller@Slf4jpublic class IndexController { @Autowired AccountService accountService; @ResponseBody @GetMapping(“/account”) public Account getAccount(@RequestParam(“id”) Long id){ return accountService.getAcc(id); }}
测试
===============================================================================
配置文件很重要,下面我们来看看配置文件中的参数都是什么意思!
配置文档的顶层结构如下:
-
configuration(配置)
-
properties(属性)
-
settings(设置)
-
typeAliases(类型别名)
-
typeHandlers(类型处理器)
-
objectFactory(对象工厂)
-
plugins(插件)
-
environments(环境配置)
-
environment(环境变量)
-
transactionManager(事务管理器)
-
dataSource(数据源)
-
databaseIdProvider(数据库厂商标识)
-
mappers(映射器)
mybatis可以使用properties来引入外部properties配置文件的内容
resource:引入类路径下的资源
url:引入网络路径或磁盘路径下资源
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。
前面的例子我们用的也是这种方式,写一个配置文件在外部,之后我们在全局配置文件中通过properties引入配置文件,之后通过${}来取配置文件中的值
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
这几个 是很重要的点,所以特地提出来
| 设置名 | 描述 | 有效值 | 默认值 |
| :-- | :-- | :-- | :-- |
| cacheEnabled | 该配置影响所有映射器中配置的缓存的全局开关 | true | false | true |
| lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType
属性来覆盖该项的开关状态。 | true | false | false |
| useColumnLabel | 使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。 | true | false | true |
| defaultStatementTimeout | 设置超时时间,它决定数据库驱动等待数据库响应的秒数。 | 任意正整数 | 未设置 (null) |
| mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
驼峰命名的开启很重要,因为在数据库中的字段两个字母组成的意思的时候往往通过下划线(_)分割,但在java中都是驼峰型的。为了解决这个问题,可以开启mapUnderscoreToCamelCase 来进行数据库字段和java属性的映射。下面会有演示
settings包含很多重要的设置项
setting用来设置每一个设置项
name设置项名
value设置项取值
第一个我们开启了驼峰映射,第二个是延迟加载后面我们会说
我们来看看是怎么映射的
@Alias(“emp”)public class Employee {
private Integer id;
private String lastName;
}
我们的实体类Employee这个字段名是lastName
数据库中的这个字段是last_name
开启后,会自动把last_name映射为lastName所以查询的话是能查询成功的
这个是sql映射文件(接口式编程,我们要先在接口中定义getEmpById方法,参数设置为id)
select * from tbl_employee
where id = #{id}
测试
// 接口式编程,方便解耦
@Test
public void test01() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
// 第二种方式是先获取接口的实现类对象,之后实现接口的方法传入参数
Employee employee = mapper.getEmpById(1);
System.out.println(employee);
} finally {
openSession.close();
}
}
Employee{id=1, lastName=‘tom’, email=‘tom@caq.com’, gender=‘0’}
可以看到lastName是有值的
如果关闭驼峰,lastName这列是不显示值的如下
中间的步骤一样不在赘述,结果为:
Employee{id=1, lastName=NULL, email=‘tom@caq.com’, gender=‘0’}
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
typeAliases:别名处理器,可以为我们的java类型起别名
别名不区分大小写
typeAliases为某个java类型起别名
type:指定要起别名的类型全类名;默认别名就是类名小写;employee
alias:指定新的别名
package:为某个包下的所有类批量起别名
name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写))
批量起别名的情况下我们不想用默认的话,可以使用@Alias注解为某个类型指定新的别名
是吧,我们在Employee实体类上面加了个@Alias给它起了个别名叫emp
@Alias(“emp”)
public class Employee {}
但是呢,虽然有别名,为了项目更好的维护我们一般都使用全类名。要不然谁知道你写的别名是啥意思
无论是MyBatis在预处理语句(PreparedStatement )中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。
插件是MyBatis提供的一个非常强大的机制,我们可以通过插件来修改MyBatis的一些核心行为。插件通过动态代理机制,可以介入四大对象的任何一个方法的执行
尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:
- 每个数据库对应一个 SqlSessionFactory 实例
MyBatis可以配置多种环境,比如开发、测试和生产环境需要有不同的配置。
每种环境使用一个environment标签进行配置并指定唯一标识符
可以通过environments标签中的default属性指定
一个环境的标识符来快速的切换环境
id:指定当前环境的唯一标识
transactionManager、和dataSource都必须有
这个就很好理解了,一些环境和配置信息~
有这些类型: UNPOOLED | POOLED | JNDI | 自定义
-
UNPOOLED:不使用连接池,UnpooledDataSourceFactory
-
POOLED:使用连接池, PooledDataSourceFactory
-
JNDI: 在EJB 或应用服务器这类容器中查找指定的数据源
-
自定义:实现DataSourceFactory接口,定义数据源的获取方式。
• 实际开发中我们使用Spring管理数据源,并进行事务控制的配置来覆盖上述配置
重头戏它来了
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
我们通过在全局配置文件进行注册
这种方式有待提高,比较麻烦写一个注册一个。我们可以通过批量注册!
• 这种方式要求SQL映射文件名必须和接口名相同并且在同一目录下
=============================================================================
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在
映射文件指导着Mybatis如何进行数据库CRUD,有着非常重要的意义
cache-命名空间的二级缓存配置
cache-ref-其他命名空间缓存配置的引用
resultMap-自定义结果集映射
sql抽取可重用语句块
insert -映射插入语句
update-映射更新语句
delete -映射删除语句
select-映射查询语句
SQL映射文件一般命名为接口名+Mapper
如EmployeeMapper、EmployeeMapperpro、EmployeeMapperpromax
下面我们来分别介绍sql映射文件中的这些元素~
单个参数
可以接受基本类型,对象类型,集合类型的值。这种情况MyBatis可值接使用这个参数﹐不需要经过任何处理。
多个参数
任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1 , param2 ,0,1…,值就是参数的值。
命名参数
为参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中, key就是我们自己指定的名字
POJO
当这些参数属于我们业务POJO时,我们直接传递POJO
Map
我们也可以封装多个参数为map ,直接传递
参数位置支持的属性
javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName
实际上通常被设置的是∶
可能为空的列名指定jdbcType
#{key}:获取参数的值,预编译到SQL中。安全。
${key}:获取参数的值,拼接到SQL中。有SQL注入问题。ORDER BY ${name}
一般选择#{key}的方式来获取参数的值
Select元素来定义查询操作。
ld:唯一标识符。
用来引用这条语句,需要和接口的方法名一致
parameterType :参数类型。
可以不传,MyBatis会根据TypeHandler自动推断
resultType:返回值类型。
别名或者全类名﹐如果返回的是集合﹐定义集合中元素的类型。不能和resultMap同时使用
resultMap:自定义返回值类型
外部resultMap的命名引用。和resultType属性不能同时使用
select * from tbl_employee where id = #{id}
insert, update 和 delete和上述查询其实查不到哪里去啦,差不多都一样的啦~
insert into tbl_employee(last_name,email,gender) values(#{lastName},#{email},#{gender}) update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id = #{id} delete from tbl_employee where id = #{id}
不难,主要是我们sql语句的书写
要注意的点是,删除修改数据库中的字段,我们传的参数都是一个javabean对象
如果数我们查的数据中不只有属性还有个对象改怎么查呢?
我们可以想象如下场景:
查询Employee的同时查询员工对应的部门
场景一:
查询Employee的同时查询员工对应的部门
Employee===Department
一个员工有与之对应的部门信息;
联合查询:级联属型封装结果集
SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,d.id did,d.dept_name dept_name FROM tbl_employee e,tbl_dept d where e.d_id = d.id and e.id = 1
上述方式的确可以解决这个问题
但是你看它是不是写的太麻烦了啊,那有没有简单的方法查询呢?
association可以指定联合的javaBean对象
指定哪个属型是联合的对象
javaType指定属型对象的类型【不能省略】
使用association定义关联的单个对象的封装规则
<association property="dept"select=“com.caq.mybatis.dao.DepartmentMapper.getDeptById” column=“d_id”>
select * from tbl_employee where id = #{id}
select:调用目标的方法查询当前属性的值
column:将指定列的值传入目标方法
没错我们可以用association来进行查询,也就是分布查询。
-
先按照员工id查询员工信息
-
根据查询员工信息中的d_id值去查部门查处部门信息
-
部门设置到员工中
从这里就能看出Mybatis的灵活,处理一些很复杂的查询的时候association能帮我们解决头痛的问题
这个元素可以用来定义可重用的 SQL 代码片段,以便在其它语句中使用。 参数可以静态地(在加载的时候)确定下来,并且可以在不同的 include 元素中定义不同的参数值。
a l i a s . i d , {alias}.id, alias.id,{alias}.username,${alias}.password
select , from some_table t1 cross join some_table t2
一般我们会把重复的sql字段提取出一个sql,然后不同的语句用这个sql字段的时候我们调这个提取出来的sql即可
全局setting设置
autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求是列名和javaBean属性名一致―如果autoMappingBehavior设置为null则会取消自动映射一数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMN→>aColumn ,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase=true
自定义resultMap ,实现高级结果集映射。
一些参数的介绍
-
constructor -类在实例化时,用来注入结果到构造方法中
-
id 一个ID结果;标记结果作为ID可以帮助提高整体效能
-
result一注入到字段或JavaBean属性的普通结果
-
association 一个复杂的类型关联;许多结果将包成这种类型
嵌入结果映射
结果映射自身的关联,或者参考一个
- collection 复杂类型的集
嵌入结果映射 结果映射自身的集,或者参考一个
discriminator 使用结果值来决定使用哪个结果映射
case-基于某些值的结果映射
嵌入结果映射-这种情形结果也映射它本身,因此可以包含很多相同的元素,或者它可以参照个外部的结果映射。
select * from tbl_employee where id = #{id};
有时候,一个数据库查询可能会返回多个不同的结果集(但总体上还是有一定的联系的)。 鉴别器(discriminator)元素就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。 鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。
column指定判定的列名,javaType列值对应的java类型
男生,如果是男生,把last_name这一列的值赋给email,女生则查询出所在部门信息
<association property=“dept”
select=“com.caq.mybatis.dao.DepartmentMapper.getDeptById”
column=“d_id”>
SELECT * FROM tbl_employee WHERE id = #{id};
这个很好理解的,我还是把测试结果写出来
// 根据id分布查员工信息@Testpublic void test03() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class); Employee empByIdStep = mapper.getEmpByIdStep(4); System.out.println(empByIdStep); System.out.println(empByIdStep.getDept()); } finally { openSession.close(); }}
我们查询id为4的员工,根据我们前面写的sql映射文件,如果它的性别是男则他的邮件地址就是它的姓名,如果他的性别是女那么就能打印出她的部门信息(查部门信息又是一个新的对象我们用的association分步查询)
显示结果分别如下:
Employee{id=4, lastName=‘jerry2’, email=‘jerry2’, gender=‘1’}
null
Employee{id=1, lastName=‘tom’, email=‘tom@caq.com’, gender=‘0’}
Department{id=1, departmentName=‘开发’}
==============================================================================
动态SQL是MyBatis强大特性之一。极大的简化我们拼装SQL的操作,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。。动态SQL元素和使用JSTL或其他类似基于XML的文本处理器相似。MyBatis采用功能强大的基于OGNL的表达式来简化操作。
-
if
-
choose (when, otherwise)
-
trim (where,set)
-
foreach
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。
比如我们有很多字段,那我们写条件的时候要在where后面写很长的判断条件,那么有了if我们就可以根据if判断条件。如果条件成立则添加不添加。
遇见特殊符号其实可以写转义字符
我们可以去这里寻找特殊符号对应的转义字符!
select *
from tbl_employee
where
id=#{id}
and last_name like #{lastName}
and email =#{email}
ognl会进行字符串与数字的转换判断
and gender=#{gender}
select *
from tbl_employee
如果带了id就用id查,如果带了lastName就用lastName查,只会进入其中一个
id = #{id}
last_name like #{lastName}
email = #{email}
gender = 0
下面我们看测试文件的结果
// 测试自定义字符串截取@Testpublic void test07 () throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class); Employee employee = new Employee(null, null, null, null,null); List list = mapper.getEmpByConditionChoose(employee); for (Employee employee1 : list) { System.out.println(employee1); } } finally { openSession.close(); }}
我们什么参数也没传入,所以她就会按照默认的来查。就是gender=0的情况
所以会出现如下结果:
Employee{id=1, lastName=‘tom’, email=‘tom@caq.com’, gender=‘0’}
Employee{id=6, lastName=‘tom’, email=‘tom@qq.com’, gender=‘0’}
Employee{id=7, lastName=‘Rose’, email=‘Rose@qq.com’, gender=‘0’}
prefix=“”:前缀:trim标签体中是整个字符串拼串后的结果
prefixOverrides=“”:
前缀覆盖:去掉整个字符串前面多余的字符
suffix=“”:后缀
suffix给拼串后的整个字符串加上一个后缀
suffixOverrides=“”
后缀覆盖:去掉整个字符串后面多余的字符
把我们容易多写少写的and去掉或者自动通过关键字加上,真是好用呢!
select * from tbl_employee
id=#{id}
last_name like #{lastName} and
email =#{email} and
gender=#{gender} and
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符
你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
有个错误一定要记得,sql语句在navicat里写或者别的sql工具。
不然在idea写错个标点要排查半天搞人心态
insert into tbl_employee(last_name,email,gender,d_id) values (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
@Test public void test08 () throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL.class); ArrayList emps = new ArrayList<>(); emps.add(new Employee(null,“tim”,“tim@qq.com”,“0”,new Department(1))); emps.add(new Employee(null,“apple”,“apple@163.com.com”,“0”,new Department(2))); mapper.addEmps(emps); openSession.commit(); System.out.println(“Success!!!”); } finally { openSession.close(); } }
添加成功啦~~~
bind元素可以从OGNL表达式中创建一个变量并将其绑定到上下文。
可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
select * from tbl_employee where last_name like #{_testBind}
@Test
public void test09 () throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
EmployeeMapperDynamicSQL mapper = openSession.getMapper(EmployeeMapperDynamicSQL .class);
Employee employee = new Employee();
employee.setLastName(“%e%”);
List list = mapper.getEmpsTestInnerParameter(employee);
for (Employee employee1 : list) {
System.out.println(employee1);
}
} finally {
openSession.close();
}
}
测试结果如下
Employee{id=3, lastName=‘jerry1’, email=‘jerry1@qq.com’, gender=‘1’}
Employee{id=4, lastName=‘jerry2’, email=‘jerry2@qq.com’, gender=‘1’}
Employee{id=5, lastName=‘jerry3’, email=‘jerry3@qq.com’, gender=‘1’}
Employee{id=8, lastName=‘jerry2’, email=‘jerry2@qq.com’, gender=‘1’}
=============================================================================
缓存机制就是用来提高查询效率的,就想我们看电影一样,把它缓存下来在看就不卡了对吧,很好理解Mybatis中的缓存机制也是处于这个目的~
下面我们分别介绍下mybatis中的缓存机制:
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存。一级缓存和二级缓存。
-
1、默认情况下,只有一级缓存(SqlSession级别的缓存﹐也称为本地缓存)开启。
-
2、二级缓存需要手动开启和配置﹐他是基于namespace级别的缓存。
-
3、为了提高扩展性。MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
-
一级缓存,既是本地的缓存,作用域默认是sqlSession。当 Session flush 或 close 后, 该 Session 中的所有 Cache 将被清空。
-
本地缓存不能被关闭, 但可以调用 clearCache() 来清空本地缓存, 或者改变缓存的作用域.
-
在mybatis3.1之后, 可以配置本地缓存的作用域. 在 mybatis.xml 中配
用关键字localCacheScope
如下图所示
sqlSession级别的缓存。一级缓存是一直开启的;sqlSession级别的一个Map(如果map里有,那就直接拿。没有就再去调用)
与数据库同一次会话期间查询到的数据会放在本地缓存中
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库
一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询)
-
sqlSession不同
-
sqlSession相同,查询条件不同
-
sqlSession相同,两次查询之间执行了增删改操作
-
sqlSession相同,手动清楚了一级缓存(缓存情况)
@Testpublic void test10() throws IOException { SqlSessionFactory sqlSessionFactory = getSqlSessionFactory(); SqlSession openSession = sqlSessionFactory.openSession(); try { EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp01 = mapper.getEmpById(1); System.out.println(emp01); Employee emp02 = mapper.getEmpById(1); System.out.println(emp02); System.out.println(emp01==emp02); } finally { openSession.close(); }}
关键的输出结果如下:
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@491cc5c9]> Preparing: select * from tbl_employee where id = ? > Parameters: 1(Integer)< Columns: id, last_name, gender, email, d_id< Row: 1, tom, 0, tom@caq.com, 1<== Total: 1Employee{id=1, lastName=‘tom’, email=‘tom@caq.com’, gender=‘0’}Employee{id=1, lastName=‘tom’, email=‘tom@caq.com’, gender=‘0’}true
下面我进行清理本地缓冲的操作
也就是在源代码的基础上加上
openSession.clearCache();
然后再次查询
关键的输出结果如下:
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@491cc5c9]> Preparing: select * from tbl_employee where id = ? > Parameters: 1(Integer)< Columns: id, last_name, gender, email, d_id< Row: 1, tom, 0, tom@caq.com, 1<== Total: 1Employee{id=1, lastName=‘tom’, email=‘tom@caq.com’, gender=‘0’}Employee{id=1, lastName=‘tom’, email=‘tom@caq.com’, gender=‘0’}false
可以看到这次查询的两个对象不在是同一个了,而是两个不同的对象,所以比较对象的地址值的时候返回的是false
基于namespace级别的缓存;一个namespace对应一个二级缓存;
工作机制:
-
查询一条数据:这个数据就会被放在当前会话的一级缓存中
-
如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存
-
sqlSession—》EmployeeMapper—》Employee
DepartMentMapper===》Department
不同namespace查出的数据会被放在自己对应的缓存(map中)
使用: