(参考)MyBatis的核心配置文件org.springframework.boot spring-boot-starter-parent 2.6.3 mysql mysql-connector-java runtime org.mybatis mybatis 3.5.7 com.baomidou mybatis-plus-boot-starter 3.5.1
建议命名为mybatis-config.xml。整合Spring之后,这个配置文件可以省略,实际很少使用,了解原理即可。
核心配置文件主要用于配置连接数据库的环境以及MyBatis的全局配置信息
位于src/main/resources目录下
核心配置文件中的标签必须按照固定的顺序(可以不写,但顺序不能乱):
properties、settings、typeAliases、typeHandlers、objectFactory、objectWrapperFactory、reflectorFactory、plugins、environments、databaseIdProvider、mappers
配置
- 连接池(数据源+driverClassName)
- 配置 SqlSessionFactory(数据源+实体类别名路径+xml路径)
- 配置 MapperScannerConfigure(绑定 SqlSessionFactory 和 mapper接口路径)这一步也就是 @MapperScan 的作用
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/db_name?useUnicode=true&characterEncoding=utf-8&useSSL=false&nullCatalogMeansCurrent=true&serverTimezone=Asia/Shanghai
username: root
password: qweryt
mybatis-plus:
# mapper-locations: classpath*:/mapper*.xml 已经默认
type-aliases-package: com.bibi.domain
type-enums-package: com.bibi.domain
global-config:
db-config:
table-prefix: ops_
id-type: assign_uuid
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
auto-mapping-behavior: full
map-underscore-to-camel-case: true
# 敏感的懒加载 默认boolean未赋值,为false(启用时只要加载对象,就会加载该对象的所有属性;关闭该属性则会按需加载)
aggressive-lazy-loading: false
# 懒加载开启 默认为false
# lazy-loading-enabled: true
# 二级缓存(默认开启)
cache-enabled: true
# 一级缓存(默认开启):session;
# 关闭(选择 statement 每次查询结束都会清空缓存)
# local-cache-scope: statement
测试
package com.bibi;
import com.bibi.domain.account.BaseOrg;
import com.bibi.domain.device.Fence;
import com.bibi.mapper.BaseOrgMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class MybatisTest {
@Test
// @Transactional
public void testMybatis() {
BaseOrg baseOrg = new BaseOrg();
baseOrg.setOrgId("100003644");
BaseOrg baseOrgs = baseOrg.selectOne(Wrappers.lambdaQuery(baseOrg));
BaseOrg baseOrgs2 =
baseOrg.selectOne(Wrappers.lambdaQuery(baseOrg)); // 用同一个session,使用缓存 WHERe org_id = ?
// BaseOrg baseOrgs1 = baseOrg.selectOne(new
// LambdaQueryWrapper().eq(BaseOrg::getOrgId,"100003644")); // sql 与上面不一样 WHERe
// (org_id = ?)
System.out.println("baseOrg1 = " + baseOrgs);
System.out.println("baseOrg1 = " + baseOrgs2);
// System.out.println("baseOrg1 = " + baseOrgs1);
// List entityIds = SimpleQuery.list(Wrappers.lambdaQuery(), BaseOrg::getOrgId);
// System.out.println("entityIds = " + entityIds);
// List names =
// SimpleQuery.list(Wrappers.lambdaQuery(BaseOrg.class).like(BaseOrg::getOrgName,"总行"),
// BaseOrg::getOrgName, e ->
// Optional.ofNullable(e.getOrgName()).map(String::toUpperCase).ifPresent(e::setOrgName));
// System.out.println("names = " + names);
}
public void update() {
// 在测试类中写增删改会被自动撤销
Fence fence = new Fence();
fence.setId("1");
fence.setCreator("2");
boolean b = fence.updateById();
System.out.println("b = " + b);
}
@Autowired private BaseOrgMapper baseOrgMapper;
@Test
void testCache() {
// 不在Mapper.xml里面的无法进行二级缓存
System.out.println(baseOrgMapper.selectSimple());
System.out.println(baseOrgMapper.selectSimple());
}
@Test
void testCacheII() {
LambdaQueryWrapper eq =
new LambdaQueryWrapper().eq(BaseOrg::getOrgId, "100003644");
BaseOrg baseOrg = baseOrgMapper.selectOne(eq);
BaseOrg baseOrg2 = baseOrgMapper.selectOne(eq);
System.out.println("baseOrg = " + baseOrg);
System.out.println("baseOrg2 = " + baseOrg2);
}
}
- 特殊
// 返回[{password=123456, sex=男, id=1, age=23, username=admin},{password=123456, sex=男, id=2, age=23, username=张三}]
List
缓存
dataSource:设置数据源
属性:
type:设置数据源的类型,type=“POOLED|UNPOOLED|JNDI”
type=“POOLED”:使用数据库连接池,即会将创建的连接进行缓存,下次使用可以从缓存中直接获取,不需要重新创建
type=“UNPOOLED”:不使用数据库连接池,即每次使用连接都需要重新创建
type=“JNDI”:调用上下文中的数据源
- 懒加载(需要手动开启,一步):
aggressive-lazy-loading: false # 已经默认为false了
lazy-loading-enabled: true
property select column fetchType(p:对应填充的属性名 s:对方的方法限定名 c:当前类传过去的条件列 f:抓取类型 懒加载否)
fetchType:当开启了全局的延迟加载之后,可以通过该属性手动控制延迟加载的效果,fetchType=“lazy(延迟加载)|eager(立即加载)”
-
自定义映射 resultMap
- association:多对一(对象组合) (property 当前类填充属性,javaType)
-
collection:一对多 (list/set 组合) (property ofType)
select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}
select * from t_emp where eid = #{eid}
@Param 源码
-
一级缓存:sqlSession级别
默认开启,在同一个sqlSession里面会使用缓存 Creating a new SqlSession Closing non transactional SqlSession(was not registered for synchronization because synchronization is not active)
mybatis的sqlSession和数据库连接池中维护的数据库连接Collection不是同一个概念,SqlSession是mybatis框架中的概念,是mybatis持久层框架的顶层API。
在sqlSession中操作数据库的时候会去获取collection,collection的获取是去连接池中取的!所以Creating a new SqlSession并不是每次都去创建了数据库新连接,底层使用的collection还是连接池提供的。至于每次事务执行sql,mybatis都Creating a new SqlSession而不是共享SqlSession,是为了保证sql会话独立避免发生脏数据,从而保证会话线程安全。
-
二级缓存:sqlSessionFactory级别
1. 在核心配置文件中,设置全局配置属性cacheEnabled="true",默认为true,不需要设置 2. 在映射文件中设置标签`
` 3. 二级缓存必须在SqlSession关闭或提交之后有效 4. 查询的数据所转换的实体类类型必须实现序列化的接口 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
刷新默认为 增删改语句挂钩,除非设置 flushInterval 属性(刷新间隔,单位毫秒)
size 设置缓存数目(正整数)
readOnly:缓存取出来是拷贝一份给他(false:默认,安全)还是直接把缓存实例给他(true:性能快) -
查询顺序:先查询二级,再查询一级
sqlSession关闭之后,一级缓存中的数据会写入二级缓存 -
二级缓存相关配置
cache标签可以设置一些属性 - eviction属性:缓存回收策略 - LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。 - FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。 - SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 - WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 - 默认的是 LRU - flushInterval属性:刷新间隔,单位毫秒。默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新 - size属性:引用数目,正整数。代表缓存最多可以存储多少个对象,太大容易导致内存溢出 - readOnly属性:只读,true/false - true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。 - false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false - type:设置二级缓存类型,如
- 声明sql片段:
标签
eid,emp_name,age,sex,email
- 引用sql片段:
标签 + refid
事务select from t_emp
不需要手动启用事务 @EnableTransactionManagement,因为Spring自动配置已经自动开启了:org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
mybatis 插件机制MyBatis有四大核心对象:
(1)ParameterHandler:处理SQL的 参数对象
(2)ResultSetHandler:处理SQL的 返回结果集
(3)StatementHandler:数据库的 处理对象 ,用于执行SQL语句(可以理解为包含sql语句)
(4)Executor:MyBatis的 执行器,用于执行 crud 操作
注意,jdbc有提供一种预编译对象 PreparedStatement
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;
import java.sql.Connection;
import java.util.Properties;
@Intercepts(
value = {
@Signature(
type = StatementHandler.class, // 确定要拦截的对象
method = "prepare", // 确定要拦截的方法
args = {Connection.class, Integer.class} // 拦截方法的参数
)
})
@Component
public class MyInterceptor implements Interceptor {
@SuppressWarnings("unused")
private Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println("开始拦截.....");
Object proceed = invocation.proceed();
System.out.println("结束拦截.....");
return proceed;
}
@Override
public Object plugin(Object target) {
System.out.println("生成代理对象...." + target);
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
System.out.println("who: " + properties.get("who"));
this.properties = properties;
}
}



