- Mybatis 2021年8月30日
- 一、简介
- 什么是 MyBatis?
- 获取mybatis
- 持久化
- 为什么要持久化?
- 持久层
- 为什么要使用mybatis?
- 二、编写第一个mybatis程序
- 搭建环境
- maven pom.xml
- usermapper接口
- usermapper.xml
- user
- mybatis-config.xml
- util 工具类
- CURD
- mapper.xml内容配置
- 1、nameSpace
- 2、select、insert、delete、update
- 通过Map传递参数
- 模糊查询
- 三、配置解析
- 1.核心配置文件
- 2.环境配置
- 3. 映射器(mappers)
- 4.属性(properties)
- 5. 类型别名(typeAliases)
- 6. 设置(settings)
- 7.其他配置
- 8. 探究已映射的 SQL 语句
- 9. 作用域(Scope)和生命周期
- SqlSessionFactoryBuilder
- SqlSessionFactory
- SqlSession
- 四、解决属性名和字段名不一致的问题
- 问题
- 解决方法
- 1. 起别名
- 2. resultMap
- 结果映射
- 高级结果映射
- 结果映射(resultMap)
- 字符串替换
- 五、日志
- 日志工厂
- 使用STDOUT_LOGGING标准日志输出
- 使用log4J
- 第一步
- 第二步 配置文件
- 第三步 添加配置
- 第四步 简单使用
- log4J的一些配置信息
- 控制台的一些信息
- 六、分页
- 七、注解开发
- 关于@param()注解
- 八、mybatis详细执行流程
- 九、lombok
- 十、多对一、一对多
- 查询多对一
- 一对多
- 动态SQL
- 1、if
- 2、where
- 3、choose(when、otherwise)
- 4、tirm
- 5、set
- 6、foreach
- 缓存
- 简介
- Mybatis缓存
- 使用方法
- 刷新缓存的情况
- 缓存原理
- 使用自定义缓存
- 手写缓存
- 第三方缓存
- cache-ref
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了[google code](https://baike.baidu.com/item/google code/2346604),并且改名为MyBatis 。
- 2013年11月迁移到Github。
- iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
- 当前,最新版本是MyBatis 3.5.7 ,其发布时间是2021年4月21日。
-
maven:Maven Repository: org.mybatis » mybatis (mvnrepository.com)
-
org.mybatis mybatis 3.5.7
-
-
GitHub:mybatis/mybatis-3: MyBatis SQL mapper framework for Java (github.com)
-
中文文档:mybatis – MyBatis 3 | 简介
数据持久化
- 持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 内存: 断电即失
- 数据库(JDBC),IO文件持久化
- 生活:冷藏,罐头
- 有一些数据不想要丢失
- 内存太贵了
Dao层,Service层,Controller层…
- 完成持久化工作的代码块
- 层界线十分明显
- 持久化是一个过程,持久层是一个概念
- 方便
- 传统的JDBC代码十分复杂。简化、框架、自动化。
- 不用mybatis也可以,更容易上手。技术没有高低之分
- 优点
- 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个jar文件+配置几个sql映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
- 灵活:mybatis不会对应用程序或者数据库的现有设计强加任何影响。
- 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。
- sql和代码的分离,提高了可维护性: sql写在xml里,便于统一管理和优化。通过sql语句可以满足操作数据库的所有需求。
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql。
目录结构
maven pom.xmlusermapper接口mysql mysql-connector-java 8.0.23 org.mybatis mybatis 3.5.7 junit junit 4.13.2 test src/main/java ***.xml true
package com.ziop.mapper;
import com.ziop.pojo.User;
import java.util.List;
public interface UserMapper {
List getUserList();
}
usermapper.xml
user
package com.ziop.pojo;
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + ''' +
", password='" + password + ''' +
'}';
}
}
mybatis-config.xml
util 工具类
package com.ziop.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class SqlSessionUtil {
public static T getMapper(Class t) throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory.openSession(true).getMapper(t);
}
}
测试类
package com.ziop.mapper;
import com.ziop.pojo.User;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class UserMapperTest {
@Test
public void getUserList() throws IOException {
// 方式一
UserMapper mapper = SqlSessionUtil.getMapper(UserMapper.class);
List users = mapper.getUserList();
for (User user : users) {
System.out.println(user);
}
// 方式二 <不推荐使用>
SqlSession session = SqlSessionUtil.getSession();
List userList = session.selectList("com.ziop.mapper.UserMapper.getUserList");
for (User user : userList) {
System.out.println(user);
}
}
}
可能会出现的错误
-
配置文件没有注册
-
在mybatis-config.xml当中
-
-
-
绑定接口错误
-
方法名不对
-
返回值类型不对
-
Maven导出资源问题
-
src/main/java ***.xml true
-
-
关于在变量中使用#{} 还是 ${}
- #{} 相当于preparestatement的情况, 会转换成字符串的情况
- ${}相当于 statement 会直接替换变量
1、nameSpace
namespace中的包名要和mapper接口的包名一致
2、select、insert、delete、update选择查询语句;
- id: 就是对应的namespace中的方法名
- resultType: SQL语句执行的返回值! class,int等等
- parameterType: 参数类型
案例
- 接口中增加的查询方法
User getUserById(int id);
mapper的xml中的SQL语句配置 ,其中的id 是值得user类的属性, 之所以我们可以直接用user而不需要添加全类名,是因为,我们之前在mybatis-confi.xml 中配置了[别名](# mybatis-config.xml)
通过Map传递参数假设,我们的实体类,或者数据库的表,字段或者参数过多,我们应当考虑使用Map!
int insertUser2(Mapmap);
insert into mybatis.user values(#{id},#{name},#{password});
@Test public void insertUserMap() throws IOException { SqlSession session = SqlSessionUtil.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); HashMap map = new HashMap<>(); map.put("id", 5); map.put("name", "12345"); map.put("password", "1235645"); int i = mapper.insertUser2(map); System.out.println(i); session.close(); }
Map传递参数,直接在SQL中取出key 即可! 【parameterType = “map”】
对象传递参数,直接在SQL中取出对象的属性即可! 【parameterType = “user”】
只有一个基本类型的参数的情况下,可以直接在SQL中取到 【】
多个参数用Map 或者 注解!
模糊查询Java中使用通配符
List三、配置解析 1.核心配置文件userLike = mapper.getUserLike("123%");
- mybatis-config.xml
- configuration(配置)
- properties(属性) 【掌握】
- settings (设置) 【部分掌握】
- typeAliases (类型别名) 【掌握】 【不难】
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins (插件)
- environments(环境配置)【会用认识】
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseProvider(数据库厂商表示)
- mappers(映射器) 【掌握】
- configuration(配置)
-
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。
-
例如,开发、测试和生产环境需要有不同的配置;
- 或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:
- 每个数据库对应一个 SqlSessionFactory 实例
为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);
如果忽略了环境参数,那么将会加载默认环境,如下所示:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);
environments 元素定义了如何配置环境。
注意一些关键点:
- 默认使用的环境 ID(比如:default=“development”)。
- 每个 environment 元素定义的环境 ID(比如:id=“development”)。
- 事务管理器的配置(比如:type=“JDBC”)。
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
- 数据源的配置(比如:type=“POOLED”)。
- UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。
- POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
- JNDI – 【废了EJB的东西】 – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。
3. 映射器(mappers) 既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。
- 使用相对于类路径的资源引用 (无位置和名称的限制)【推荐使用】
- 完全限定资源定位符(包括 file:/// 形式的 URL)【不推荐使用】
- 类名 (接口和xml配置文件必须在同一个包下,并且同名)
- 包名 (接口和xml配置文件必须在同一个包下,并且同名)
例如:
这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要讨论的。
4.属性(properties)注意: 在使用mybatis 的properties的时候,需要配置maven的资源路径,要不然加载不到资源路径
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。例如:
设置好的属性可以在整个配置文件中用来替换需要动态配置的属性值。比如:
这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。
也可以在 SqlSessionFactoryBuilder.build() 方法中传入属性值。例如:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props); // ... 或者 ... SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
如果一个属性在不只一个地方进行了配置,那么,MyBatis 将按照下面的顺序来加载:
- 首先读取在 properties 元素体内指定的属性。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的则是 properties 元素中指定的属性。
从 MyBatis 3.4.2 开始,你可以为占位符指定一个默认值。例如:
这个特性默认是关闭的。要启用这个特性,需要添加一个特定的属性来开启这个特性。例如:
提示 如果你在属性名中使用了 ":" 字符(如:db:username),或者在 SQL 映射中使用了 OGNL 表达式的三元运算符(如: ${tableName != null ? tableName : 'global_constants'}),就需要设置特定的属性来修改分隔属性名和默认值的字符。例如:
5. 类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
每一个在包 domain.blog 中的 Java Bean,
-
在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。比如 domain.blog.Author 的别名为 author;
-
若有注解,则别名为其注解值。
见下面的例子:
@Alias("author")
public class Author {
...
}
下面是一些为常见的 Java 类型内建的类型别名。它们都是不区分大小写的,注意,为了应对原始类型的命名重复,采取了特殊的命名风格。
| 别名 | 映射的类型 |
|---|---|
| _byte | byte |
| _long | long |
| _short | short |
| _int | int |
| _integer | int |
| _double | double |
| _float | float |
| _boolean | boolean |
| string | String |
| byte | Byte |
| long | Long |
| short | Short |
| int | Integer |
| integer | Integer |
| double | Double |
| float | Float |
| boolean | Boolean |
| date | Date |
| decimal | BigDecimal |
| bigdecimal | BigDecimal |
| object | Object |
| map | Map |
| hashmap | HashMap |
| list | List |
| arraylist | ArrayList |
| collection | Collection |
| iterator | Iterator |
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
| 设置名 | 描述 | 有效值 | 默认值 |
|---|---|---|---|
| cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
| lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。 | true | false | false |
| useGeneratedKeys | 允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
| mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true | false | False |
| logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
-
typeHandlers(类型处理器)
-
objectFactory(对象工厂)
-
plugins(插件)
-
mybatis-plus
-
com.baomidou mybatis-plus Latest Version
-
-
强调:一个语句既可以通过 XML 定义,也可以通过注解定义。
我们先看看 XML 定义语句的方式,事实上 MyBatis 提供的所有特性都可以利用基于 XML 的映射语言来实现,这使得 MyBatis 在过去的数年间得以流行。
如果你用过旧版本的 MyBatis,你应该对这个概念比较熟悉。 这里给出一个基于 XML 映射语句的示例,它应该可以满足上个示例中 SqlSession 的调用。
为了这个简单的例子,我们似乎写了不少配置,但其实并不多。
在一个 XML 映射文件中,可以定义无数个映射语句,这样一来,XML 头部和文档类型声明部分就显得微不足道了。
文档的其它部分很直白,容易理解。
- 它在命名空间 “org.mybatis.example.BlogMapper” 中定义了一个名为 “selectBlog” 的映射语句,这样你就可以用全限定名 “org.mybatis.example.BlogMapper.selectBlog” 来调用映射语句了,就像上面例子中那样:
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
你可能会注意到,这种方式和用全限定名调用 Java 对象的方法类似。
这样,该命名就可以直接映射到在命名空间中同名的映射器类,并将已映射的 select 语句匹配到对应名称、参数和返回类型的方法。
因此你就可以像上面那样,不费吹灰之力地在对应的映射器接口调用方法,就像下面这样:
BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = mapper.selectBlog(101);
第二种方法有很多优势,首先它不依赖于字符串字面值,会更安全一点;
其次,如果你的 IDE 有代码补全功能,那么代码补全可以帮你快速选择到映射好的 SQL 语句。
提示 对命名空间的一点补充
在之前版本的 MyBatis 中,**命名空间(Namespaces)**的作用并不大,是可选的。 但现在,随着命名空间越发重要,你必须指定命名空间。
命名空间的作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了你上面见到的接口绑定。就算你觉得暂时用不到接口绑定,你也应该遵循这里的规定,以防哪天你改变了主意。 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,你的代码会变得更加整洁,也有利于你更方便地使用 MyBatis。
**命名解析:**为了减少输入量,MyBatis 对所有具有名称的配置元素(包括语句,结果映射,缓存等)使用了如下的命名解析规则。
- 全限定名(比如 “com.mypackage.MyMapper.selectAllThings)将被直接用于查找及使用。
- 短名称(比如 “selectAllThings”)如果全局唯一也可以作为一个单独的引用。 如果不唯一,有两个或两个以上的相同名称(比如 “com.foo.selectAllThings” 和 “com.bar.selectAllThings”),那么使用时就会产生“短名称不唯一”的错误,这种情况下就必须使用全限定名。
9. 作用域(Scope)和生命周期
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
提示 对象生命周期和依赖注入框架
-
依赖注入框架可以创建线程安全的、基于事务的 SqlSession 和映射器,并将它们直接注入到你的 bean 中,因此可以直接忽略它们的生命周期。
-
如果对如何通过依赖注入框架使用 MyBatis 感兴趣,可以研究一下 MyBatis-Spring 或 MyBatis-Guice 两个子项目。
SqlSessionFactoryBuilder
-
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。
-
因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
-
你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
- 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”
- 因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
- 每个线程都应该有它自己的 SqlSession 实例。
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
- 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
- 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。
- 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。
- 所以sqlsession每次使用之后都要关闭一下。
上面写的是resultType ,我们接下来要使用的是resultMap,
映射的时候只需要写不一样的映射就行了
结果映射resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
之前你已经见过简单映射语句的示例,它们没有显式指定 resultMap。比如:
上述语句只是简单地将所有的列映射到 HashMap 的键上,这由 resultType 属性指定。虽然在大部分情况下都够用,但是 HashMap 并不是一个很好的领域模型。你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 对象)作为领域模型。MyBatis 对两者都提供了支持。看看下面这个 JavaBean:
package com.someapp.model;
public class User {
private int id;
private String username;
private String hashedPassword;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getHashedPassword() {
return hashedPassword;
}
public void setHashedPassword(String hashedPassword) {
this.hashedPassword = hashedPassword;
}
}
基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些属性会对应到 select 语句中的列名。
这样的一个 JavaBean 可以被映射到 ResultSet,就像映射到 HashMap 一样简单。
类型别名是你的好帮手。使用它们,你就可以不用输入类的全限定名了。比如:
在这些情况下,MyBatis 会在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上。如果列名和属性名不能匹配上,可以在 SELECT 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配。比如:
在学习了上面的知识后,你会发现上面的例子没有一个需要显式配置 ResultMap,这就是 ResultMap 的优秀之处——你完全可以不用显式地配置它们。 虽然上面的例子不用显式配置 ResultMap。 但为了讲解,我们来看看如果在刚刚的示例中,显式使用外部的 resultMap 会怎样,这也是解决列名不匹配的另外一种方式。
然后在引用它的语句中设置 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:
如果这个世界总是这么简单就好了。
高级结果映射MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
比如,我们如何映射下面这个语句?
你可能想把它映射到一个智能的对象模型,这个对象表示了一篇博客,它由某位作者所写,有很多的博文,每篇博文有零或多条的评论和标签。 我们先来看看下面这个完整的例子,它是一个非常复杂的结果映射(假设作者,博客,博文,评论和标签都是类型别名)。 不用紧张,我们会一步一步地来说明。虽然它看起来令人望而生畏,但其实非常简单。
resultMap 元素有很多子元素和一个值得深入探讨的结构。 下面是resultMap 元素的概念视图。
结果映射(resultMap)-
constructor用于在实例化类时,注入结果到构造方法中
- idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
- arg - 将被注入到构造方法的一个普通结果
-
id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
-
result – 注入到字段或 JavaBean 属性的普通结果
-
association
-
一个复杂类型的关联;许多结果将包装成这种类型
- 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
-
collection 一个复杂类型的集合
- 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
-
discriminator 使用结果值来决定使用哪个resultMap
- case– 基于某些值的结果映射
- 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
- case– 基于某些值的结果映射
| 属性 | 描述 |
|---|---|
| id | 当前命名空间中的一个唯一标识,用于标识一个结果映射。 |
| type | 类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。 |
| autoMapping | 如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。 |
最佳实践
-
最好逐步建立结果映射。
-
单元测试可以在这个过程中起到很大帮助。
-
如果你尝试一次性创建像上面示例那么巨大的结果映射,不仅容易出错,难度也会直线上升。 所以,从最简单的形态开始,逐步迭代。
-
而且别忘了单元测试! 有时候,框架的行为像是一个黑盒子(无论是否开源)。因此,为了确保实现的行为与你的期望相一致,最好编写单元测试。
-
并且单元测试在提交 bug 时也能起到很大的作用。
默认情况下,使用 #{} 参数语法时,MyBatis 会创建 PreparedStatement 参数占位符,并通过占位符安全地设置参数(就像使用 ? 一样)。 这样做更安全,更迅速,通常也是首选做法,不过有时你就是想直接在 SQL 语句中直接插入一个不转义的字符串。 比如 ORDER BY 子句,这时候你可以:
ORDER BY ${columnName}
这样,MyBatis 就不会修改或转义该字符串了。
当 SQL 语句中的元数据(如表名或列名)是动态生成的时候,字符串替换将会非常有用。 举个例子,如果你想 select 一个表任意一列的数据时,不需要这样写:
@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);
@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);
@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);
// 其它的 "findByXxx" 方法
而是可以只写这样一个方法:
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);
其中 ${column} 会被直接替换,而 #{value} 会使用 ? 预处理。 这样,就能完成同样的任务:
User userOfId1 = userMapper.findByColumn("id", 1L);
User userOfNameKid = userMapper.findByColumn("name", "kid");
User userOfEmail = userMapper.findByColumn("email", "noone@nowhere.com");
这种方式也同样适用于替换表名的情况。
提示 用这种方式接受用户的输入,并用作语句参数是不安全的,会导致潜在的 SQL 注入攻击。因此,要么不允许用户输入这些字段,要么自行转义并检验这些参数。
五、日志 日志工厂如果一个数据库操作,出现了异常,我们需要排错,日志是最好的助手!
曾经的,sout、debug
现在的日志工厂!
| logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未设置 |
|---|
- SLF4J
- LOG4J 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
在mybatis中具体使用一个日志实现,
测试代码
@Test
public void getUserById() throws IOException {
SqlSession session = SqlSessionUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User users = mapper.getUserById(1);
System.out.println(users);
session.close();
}
使用STDOUT_LOGGING标准日志输出
Opening JDBC Connection
Created connection 1190820921.
==> Preparing: select * from mybatis.user where id = 1;
==> Parameters:
<== Columns: id, name, password
<== Row: 1, 张三, 123456
<== Total: 1
User{id=1, name='张三', password='123456'}
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@46fa7c39]
Returned connection 1190820921 to pool.
使用log4J
- Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件等;
- 我们也可以控制每一条日志的输出格式;
- 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
- 这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
加入log4j-1.2.8.jar(可以选择log4j的更高版本)到lib下。
如果使用maven项目,也可以选择在pom.xml中新增依赖:
第二步 配置文件log4j log4j 1.2.17
在CLASSPATH下建立log4j.properties。内容如下:
# Global logging configuration
# 设置日志输出级别以及输出目的地,可以设置多个输出目的地,开发环境下,日志级别要设置成DEBUG或者ERROR
# 前面写日志级别,逗号后面写输出目的地:我自己下面设置的目的地相对应,以逗号分开
# log4j.rootLogger = [level],appenderName1,appenderName2,…
log4j.rootLogger=DEBUG,CONSOLE,LOGFILE
#### 控制台输出 ####
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
# 输出到控制台
log4j.appender.CONSOLE.Target = System.out
# 指定控制台输出日志级别
log4j.appender.CONSOLE.Threshold = DEBUG
# 默认值是 true, 表示是否立即输出
log4j.appender.CONSOLE.ImmediateFlush = true
# 设置编码方式
log4j.appender.CONSOLE.Encoding = UTF-8
# 日志输出布局
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
# 如果日志输出布局为PatternLayout 自定义级别,需要使用ConversionPattern指定输出格式
log4j.appender.CONSOLE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %5p (%c:%L) - %m%n
#### 输出错误信息到文件 ####
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
# 指定输出文件路径
#log4j.appender.LOGFILE.File =F://Intellij idea/logs/error.log
log4j.appender.LOGFILE.File =./logs/error.log
#日志输出到文件,默认为true
log4j.appender.LOGFILE.Append = true
# 指定输出日志级别
log4j.appender.LOGFILE.Threshold = ERROR
# 是否立即输出,默认值是 true,
log4j.appender.LOGFILE.ImmediateFlush = true
# 设置编码方式
log4j.appender.LOGFILE.Encoding = UTF-8
# 日志输出布局
log4j.appender.LOGFILE.layout = org.apache.log4j.PatternLayout
# 如果日志输出布局为PatternLayout 自定义级别,需要使用ConversionPattern指定输出格式
log4j.appender.LOGFILE.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
第三步 添加配置
mybatis-config.xml
第四步 简单使用
在要输出日志的类中加入相关语句:
定义属性:
static Logger logger = Logger.getLogger(LogDemo.class); //LogDemo为相关的类
在相应的方法中:
if (logger.isDebugEnabled()){
logger.debug(“System …..”);
}
测试类
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.junit.Test;
public class Log4jTest {
//创建日志对象 Logger
static final Logger logger = Logger.getLogger(Log4jTest.class);
@Test
public void testLog4j(){
//显示警告级别以上的信息
logger.setLevel(Level.WARN);
// debug level
logger.debug("DEBUG(调试)");
// info level
logger.info("INFO(消息)");
// warning level
logger.warn("WARN(警告)");
// error level
logger.error("ERROR(错误)");
}
}
log4J的一些配置信息
log4j.rootLogger = [ level ] , appenderName, appenderName, …
2.配置日志信息输出目的地Appender,其语法为:
log4j.appender.appenderName = fully.qualified.name.of.appender.class log4j.appender.appenderName.option1 = value1 … log4j.appender.appenderName.option = valueN
其中,Log4j提供的appender有以下几种:
org.apache.log4j.ConsoleAppender(控制台), org.apache.log4j.FileAppender(文件), org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件), org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件), org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
例如:
log4j.appender.ConSOLE=org.apache.log4j.ConsoleAppender log4j.appender.LOGFILE=org.apache.log4j.FileAppender
3.配置日志信息的格式(布局),其语法为:
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class log4j.appender.appenderName.layout.option1 = value1 … log4j.appender.appenderName.layout.option = valueN
其中,Log4j提供的layout有以e几种:
org.apache.log4j.HTMLLayout(以HTML表格形式布局), org.apache.log4j.PatternLayout(可以灵活地指定布局模式), org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串), org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
Log4J采用类似C语言中的printf函数的打印格式格式化日志信息,打印参数如下: %m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数。举例:Testlog4.main(TestLog4.java:10)
控制台的一些信息
Threshold=DEBUG:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
Target=System.err:默认情况下是:System.out,指定输出控制台
FileAppender 选项
Threshold=DEBUF:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
File=mylog.txt:指定消息输出到mylog.txt文件。
Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
RollingFileAppender 选项
Threshold=DEBUG:指定日志消息的输出最低层次。
ImmediateFlush=true:默认值是true,意谓着所有的消息都会被立即输出。
File=mylog.txt:指定消息输出到mylog.txt文件。
Append=false:默认值是true,即将消息增加到指定文件中,false指将消息覆盖指定的文件内容。
MaxFileSize=100KB: 后缀可以是KB, MB 或者是 GB. 在日志文件到达该大小时,将会自动滚动,即将原来的内容移到mylog.log.1文件。
MaxBackupIndex=2:指定可以产生的滚动文件的最大数。
log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %d{yyyy-MM-dd HH:mm:ssS} %c %m%n
六、分页
分页的目的
- 减少查询的数据量
使用limit语法
select * from user limit 3,2;
-
写接口
-
注册mapper
-
select * from mybatis.user limit #{startIndex},#{pageSize};
-
-
测试
使用RowBounds
-
写接口
-
注册mapper
-
测试
-
SqlSession session = SqlSessionUtil.getSession(); RowBounds rowBounds = new RowBounds(1, 1); List
-
简单的映射,直接在接口中添加相应的注解就行,但是复杂的映射我们还是应该使用xml去配置SQL语句,例如
@Select("select * from user;")
List getUserList();
@Select("select * from user where id = #{id} and name = #{name}")
User getUserById(@Param("id") int id123,@Param("name") String name123);
映射的参数以我们写的注解为依据
关于@param()注解- 基本数据类型的参数或者String类型,都需要加上
- 引用数据类型不需要加
- 如果只有一个基本类型的话,可以忽略,建议都加上
- 我们在SQL中引用的参数就是我们在@param里面设置的属性名
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
选择何种方式来配置映射,以及认为是否应该要统一映射语句定义的形式,完全取决于你和你的团队。 换句话说,永远不要拘泥于一种方式,你可以很轻松的在基于注解和 XML 的语句映射方式间自由移植和切换。
八、mybatis详细执行流程上面中流程就是MyBatis内部核心流程,每一步流程的详细说明如下文所述:
- 读取MyBatis的配置文件。mybatis-config.xml为MyBatis的全局配置文件,用于配置数据库连接信息。
- 加载映射文件。映射文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在MyBatis配置文件mybatis-config.xml中加载。mybatis-config.xml 文件可以加载多个映射文件,每个文件对应数据库中的一张表。
- 构造会话工厂。通过MyBatis的环境配置信息构建会话工厂SqlSessionFactory。
- 创建会话对象。由会话工厂创建SqlSession对象,该对象中包含了执行SQL语句的所有方法。
- Executor执行器。MyBatis底层定义了一个Executor接口来操作数据库,它将根据SqlSession传递的参数动态地生成需要执行的SQL语句,同时负责查询缓存的维护。
- MappedStatement对象。在Executor接口的执行方法中有一个MappedStatement类型的参数,该参数是对映射信息的封装,用于存储要映射的SQL语句的id、参数等信息。
- 输入参数映射。输入参数类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输入参数映射过程类似于JDBC对preparedStatement对象设置参数的过程。
- 输出结果映射。输出结果类型可以是Map、List等集合类型,也可以是基本数据类型和POJO类型。输出结果映射过程类似于JDBC对结果集的解析过程。
导入依赖
org.projectlombok lombok 1.18.20
@Getter and @Setter @FieldNameConstants @ToString //生成tostring方法 @EqualsAndHashCode @AllArgsConstructor //生成有参构造 @RequiredArgsConstructor // @NoArgsConstructor //生成无参构造 @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data //生成get set tostring equals 无参构造 hashcode @Builder @SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val @var experimental @var @UtilityClass
测试类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private int id;
private String name;
private String password;
}
十、多对一、一对多
- 多个学生对应一个老师,
- 对于学生而言, 关联…多个学生,关联一个老师【多对一】
- 对于老师而言,**集合。。**一个老师,有很多学生【一对多】
方法一:
select * from mybatis.student;
方法二
一对多
-
关联 association 【多对一】
-
集合 collection 【一对多】
-
javaType & ofType
- javaType 用来映射实体类中属性的类型
- ofType 用来映射到List 或者集合中的pojo类型,泛型中的约束类型
注意点:
- 保证SQL的可读性,尽量通俗易懂
- 注意一对多和多对一中,属性和字段名的问题
- 如果问题不好排查错误,可以使用日志,建议使用Log4J
概念: 动态SQL就是根据不同的条件生成不同的SQL
如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
1、if缓存 简介2、whereSELECT * FROM BLOG WHERe state = ‘ACTIVE’ AND title like #{title} AND name like #{title} 像上面的那种情况,如果where后面没有条件,然后需要直接写if判断(开头如果是 and / or 的话,会去除掉)
3、choose(when、otherwise)SELECT * FROM BLOG AND title like #{title} AND name like #{title} choose 相当于 java 里面的 switch 语句。otherwise(其他情况)
4、tirmSELECT * FROM BLOG WHERe state = ‘ACTIVE’ AND title like #{title} AND author_name like #{author.name} AND featured = 1 tirm
prefix:前缀prefixoverride:去掉第一个and或者是or
select * from test5、setAND a=#{a} AND a=#{a} set 元素主要是用在更新操作的时候,如果包含的语句是以逗号结束的话将会把该逗号忽略,如果set包含的内容为空的话则会出错。
6、foreachupdate t_blog where id = #{id} title = #{title}, content = #{content}, owner = #{owner} foreach主要用在构建in条件中
select * from t_blog where id in #{item} open separator close
相当于是in (?,?,?)
如果是个map怎么办
select * from t_blog where title like "%"#{title}"%" and id in #{item} collection对应map的键,像这样
Listids = new ArrayList (); ids.add(1); ids.add(2); ids.add(3); ids.add(6); ids.add(7); ids.add(9); Map params = new HashMap (); params.put("ids", ids);
查询 : 连接数据库,十分的耗费资源
一次查询的结果,给她暂存在一个可以直接取到的地方! --> 内存 : 缓存
我们再次查询相同的数据的时候, 直接走缓存就行,不用再走数据库
缓存(cache),原始意义是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。
读写分离,主从复制
Mybatis缓存
- 什么是缓存 [ Cache ]?
- 存在内存中的临时数据。
- 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
- 为什么使用缓存?
- 减少和数据库的交互次数,减少系统开销,提高系统效率。
- 什么样的数据能使用缓存?
- 经常查询并且不经常改变的数据。
MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存:
一级缓存
和
二级缓存
- 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
- 二级缓存的时候需要将实体类进行序列化 实现Serializable接口
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
使用方法刷新缓存的情况默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的SQL 映射文件中添加一行:
在mybatis-config.xml中添加
基本上就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
提示 缓存只作用于 cache 标签所在的映射文件中的语句。如果你混合使用 Java API 和 XML 映射文件,在共用接口中的语句将不会被默认缓存。你需要使用 @CacheNamespaceRef 注解指定缓存作用域。
这些属性可以通过 cache 元素的属性来修改。比如:
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
- LRU – 最近最少使用:移除最长时间不被使用的对象。
- FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
- SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
- WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU。
flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。
提示 二级缓存是事务性的。这意味着,当 SqlSession 完成并提交时,或是完成并回滚,但没有执行 flushCache=true 的 insert/delete/update 语句时,缓存会获得更新。
缓存原理 使用自定义缓存使用方法
手写缓存
第三方缓存除了上述自定义缓存的方式,你也可以通过实现你自己的缓存,或为其他第三方缓存方案创建适配器,来完全覆盖缓存行为。
这个示例展示了如何使用一个自定义的缓存实现。type 属性指定的类必须实现 org.apache.ibatis.cache.Cache 接口,且提供一个接受 String 参数作为 id 的构造器。 这个接口是 MyBatis 框架中许多复杂的接口之一,但是行为却非常简单。
public interface Cache { String getId(); int getSize(); void putObject(Object key, Object value); Object getObject(Object key); boolean hasKey(Object key); Object removeObject(Object key); void clear(); }为了对你的缓存进行配置,只需要简单地在你的缓存实现中添加公有的 JavaBean 属性,然后通过 cache 元素传递属性值,例如,下面的例子将在你的缓存实现上调用一个名为 setCacheFile(String file) 的方法:
你可以使用所有简单类型作为 JavaBean 属性的类型,MyBatis 会进行转换。 你也可以使用占位符(如 ${cache.file}),以便替换成在配置文件属性中定义的值。
从版本 3.4.2 开始,MyBatis 已经支持在所有属性设置完毕之后,调用一个初始化方法。 如果想要使用这个特性,请在你的自定义缓存类里实现 org.apache.ibatis.builder.InitializingObject 接口。
public interface InitializingObject { void initialize() throws Exception; }提示 上一节中对缓存的配置(如清除策略、可读或可读写等),不能应用于自定义缓存。
请注意,缓存的配置和缓存实例会被绑定到 SQL 映射文件的命名空间中。 因此,同一命名空间中的所有语句和缓存将通过命名空间绑定在一起。 每条语句可以自定义与缓存交互的方式,或将它们完全排除于缓存之外,这可以通过在每条语句上使用两个简单属性来达成。 默认情况下,语句会这样来配置:
鉴于这是默认行为,显然你永远不应该以这样的方式显式配置一条语句。但如果你想改变默认的行为,只需要设置 flushCache 和 useCache 属性。比如,某些情况下你可能希望特定 select 语句的结果排除于缓存之外,或希望一条 select 语句清空缓存。类似地,你可能希望某些 update 语句执行时不要刷新缓存。
如果没有去配置flushCache、useCache,那么默认是启用缓存的
- flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存。
- useCache默认为true,表示会将本条语句的结果进行二级缓存。
- 在insert、update、delete语句时: flushCache默认为true,表示任何时候语句被调用,都会导致本地缓存和二级缓存被清空。 useCache属性在该情况下没有。update 的时候如果 flushCache=“false”,则当你更新后,查询的数据数据还是老的数据。
回想一下上一节的内容,对某一命名空间的语句,只会使用该命名空间的缓存进行缓存或刷新。 但你可能会想要在多个命名空间中共享相同的缓存配置和实例。要实现这种需求,你可以使用 cache-ref 元素来引用另一个缓存。
本文章是观看狂神的mybatis的视频跟着讲课进度做出的笔记,意在整理笔记方便自己也方便其他人快捷观看。狂神的mybatis视频可以直接在B站搜索“mybatis 狂神”就能搜到,最近在学习狂神的spring boot,有兴趣的大家可以一起讨论。



