一. 测试基本准备:
1. Springboot项目: 快速创建Spring Initializer
2. 导入依赖: starter
3. yml配置:
(1). dataSource四大属性
(2). mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout. StaOutImpl #开启SQL语句打印
4. 实体类: @Data //lombok注解 -> 类的setter/getter/equals/hashCode/toString方法
5. 数据库表user
6. mapper接口: public interface UserMapper extends BaseMapper<User>{}
7. 启动类mapper接口扫描: @MapperScan("com.changgou.user.dao")
8. 测试类: @SpringBootTest
1. 结论: 针对单表CRUD操作 -> MP自动做数据库下划线命名, 驼峰命名之间的转化;
二. 核心:
1. 8个注解:
@TableName(name="user") //类映射数据库
public class User {
@TableId(value="id") //字段 -> 主键id
private Integer id;
@TableField(exist=false) //额外字段, 插入时忽略字段
private transient String unnecessary;
@TableField(insertStragtegy / updateStrategy / whereStrategy) //控制对象字段如何组装SQL
@TableField(fill) //自动填充
@Version //乐观锁
@EnumValue //枚举字段
@TableLogic //逻辑删除
@keySequence //oracle主键不自增, 配置序列主键策略
@InterceptorIgnore //拦截器过滤规则
}
2. CRUD接口: Mapper CRUD/Service CRUD接口 (封装CRUD方法+条件构造器Wrapper)
Mapper CRUD接口:
Public interface UserMapper extends BaseMapper<User>{
(1) Mybatis启动 -> MP解析实体类和表关系映射 -> 注入CRUD mapper
insert(T entity) 插入一条记录
deleteById(Serializable id) 根据主键id删除一条记录
delete(Wrapper<T> wrapper) 根据条件构造器wrapper进行删除
selectById(Serializable id) 根据主键id进行查找
selectBatchIds(Collection idList) 根据主键id进行批量查找
selectByMap(Map<String,Object> map) 根据map中指定的列名和列值进行等值匹配查找
selectMaps(Wrapper<T> wrapper) 根据 wrapper 条件,查询记录,将查询结果封装为一个Map,Map的key为结果的列,value为值
selectList(Wrapper<T> wrapper) 根据条件构造器wrapper进行查询
update(T entity, Wrapper<T> wrapper) 根据条件构造器wrapper进行更新
updateById(T entity)
(2) selectMap: 查询指定列, 返回Map<key是列名String, value是列数据Object>
(3) selectObjs: 只返回第一列/字段值
(4) selectCount: 查询满足条件总数, 方法自动添加select count(1)
}
Service CRUD接口: //属于service层, 支持更多批量化操作
(1) public interface UserService extends IService<User> { }
(2) @Service
//这里的笔记看不懂, 继承的什么类?
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{}
(3) IService支持链式调用
public void TestChain(){
List<User> list = userService.lambdaQuery()
.gt(User::getAge, 39)
.likeRight(User::getName, "王")
.list();
list.forEach(System.out::println);
1. 条件构造器:
QueryWrapper: SELECT语句 -> select()方法
UpdateWrapper: UPDATE语句 -> set()方法
父接口: AbstractMapper
2. WHERE条件:
eq / allEq / ne / gt / ge / lt / le / between / notBetween
like("name", "黄"), -> name like '%黄%'
likeRight("name", "黄") -> name like '黄%'
likeLeft("name", "黄") -> name like '%黄'
notlike("name", "黄") -> name not like '%黄%'
isNull
isNotNull
in
and
apply: 拼接SQL, 数据库函数, 动态传参
3. 练习了解:
new QueryWrapper<>().like("name","黄").lt("age", 25).isNotNull("email");
wrapper.likeRight("name","黄").or().ge("age",40).orderByDesc("age").orderByAsc("id");
4. 指定boolean类型的参数condition -> true则加入条件
//hasText为true时, 拼接like -> where
new QueryWrapper<>().like(StringUtils.hasText(name), "name", name);
5. 新建对象 -> 属性为空 -> 赋值部分属性 -> 通过 非空属性 = 等值匹配 -> 查询
User user = new User();
user.setName("黄冰冰");
user.setAge(18);
List<User> users = userMapper.selectList(new QueryWrapper<>(user)); //精准查询
通过@TableField(condition=SqlCondition.LIKE), 使用like拼接字段
user.setName("黄");
6. lambda条件构造器:
new LamdaQueryWrapper<>().like(User::getName, "黄").lt(User::getAge, 30);
7. update(T entity, Wrapper<T> wrapper): 更加实体类和构造器更新
new LambdaUpdateWrapper<>().between(User::getAge, 26, 31).likeRight(User::getName,"吴");
8. 删除: deleteById / deleteBatchIds / deleteByMap / delete(Wrapper<T> wrapper)
9. 原生mybatis: 自定义SQL
(1) 注解: @Select("select * from user ${ew.customSqlSegment}")
(2) XML: <select id="findAll" resultType="com.example.mp.po.User">
SELECT * FROM user ${ew.customSqlSegment}
</select>
10. 分页:
(1) selectPage -> java pojo
(2) selectMapsPage -> 封装Map<String, Object>
(3) 分页拦截器: MybatisPlusInterceptor
new MybatisPlusInterceptor().addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
1. MP主键策略: @TableId(type="")
(1) 默认策略: 基于雪花算法的自增id
(2) 策略类型: 枚举类IdType = AUTO / NONE / INPUT / ASSIGN_ID
1) auto: 依赖数据库ID自增
2) none: 不设置主键类型
3) input: 手动设置主键 -> 无则值为NULL(插入操作SQL) -> Oracle
4) assign_id: 主键为空时, 自动填充
(3) yml中配置: mybatis-plus: global-config: db-config: id-type: auto
2. 配置:
(1) configLocation: 全局配置文件
(2) mapperLocation: xml文件位置
(3) typeAliasesPackage: 别名包扫描
(4) mapUnderscoreCamelCase: 约定自动驼峰式
(5) dbType: 数据库类型
(6) 字段验证策略:
1) INGNORED: 不校验
2) NOT_NULL: 非null校验
3) NOT_EMPTY: 非空校验
4) NEVER: 不加SQL
三. MP代码生成器:
public class Generator {
@Test
public void generate() {
AutoGenerator generator = new AutoGenerator();
// 全局配置
GlobalConfig config = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
// 设置输出到的目录
config.setOutputDir(projectPath + "/src/main/java");
config.setAuthor("yogurt");
// 生成结束后是否打开文件夹
config.setOpen(false);
// 全局配置添加到 generator 上
generator.setGlobalConfig(config);
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/yogurt?serverTimezone=Asia/Shanghai");
dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("root");
// 数据源配置添加到 generator
generator.setDataSource(dataSourceConfig);
// 包配置, 生成的代码放在哪个包下
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example.mp.generator");
// 包配置添加到 generator
generator.setPackageInfo(packageConfig);
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
// 下划线驼峰命名转换
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
// 开启lombok
strategyConfig.setEntityLombokModel(true);
// 开启RestController
strategyConfig.setRestControllerStyle(true);
generator.setStrategy(strategyConfig);
generator.setTemplateEngine(new FreemarkerTemplateEngine());
// 开始生成
generator.execute();
}
}
四. 高级功能:
1. 逻辑删除: 数据恢复, 保护数据
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名
logic-delete-value: 1 # 逻辑已删除值(默认为1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为0)
# 若逻辑已删除和未删除的值和默认值一样,则可以不配置这2项
//全局配置: 多个表会统一逻辑删除字段名,统一逻辑已删除和未删除的值
@TableLogic(value = "0", delval = "1")
private Integer deleted;
2. MP逻辑删除, 会对SQL产生如下的影响
- INSERT语句:没有影响
- SELECT语句:追加WHERE条件,过滤掉已删除的数据
- UPDATE语句:追加WHERE条件,防止更新到已删除的数据
- DELETE语句:转变为UPDATE语句
- 只针对mp自动注入的SQL生效。**如果是自己手动添加的自定义SQL,则不会生效
3. 自动填充: 自动填充某些字段的增改
public class User2 {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
@TableField(fill = FieldFill.INSERT) // 插入时自动填充
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE) // 更新时自动填充
private LocalDateTime updateTime;
private Integer version;
private Integer deleted;
}
4. 乐观锁插件: -> 并发操作 -> 数据冲突 -> 控制并发
(1) 悲观锁的方法是,在对数据库的一条记录进行修改时,先直接加锁(数据库的锁机制),锁定这条数据,然后再进行操作;
(2) 乐观锁,正如其名,它先假设不存在冲突情况,而在实际进行数据操作时,再检查是否冲突
(3) 乐观锁的一种通常实现是版本号,在MySQL中也有名为MVCC的基于版本号的并发事务控制
在读多写少的场景下,乐观锁比较适用,能够减少加锁操作导致的性能开销,提高系统吞吐量。
在写多读少的场景下,悲观锁比较使用,否则会因为乐观锁不断失败重试,反而导致性能下降。
(4) 乐观锁实现:
(1) 取出记录, 获取version
(2) 更新带上version
(3) 执行更新, set version=newVersion where version = oldVersion
(4) 如果oldVersion != 数据库的version, 更新失败
package com.example.mp.config;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MybatisPlusConfig {
/** 3.4.0以后的mp版本,推荐用如下的配置方式 **/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
/** 旧版mp可以采用如下方式。注意新旧版本中,新版的类,名称带有Inner, 旧版的不带, 不要配错了 **/
/*
@Bean
public OptimisticLockerInterceptor opLocker() {
return new OptimisticLockerInterceptor();
}
*/
}
实体类中表示版本的事务添加注解 @Version
@Data
public class User2 {
private Long id;
private String name;
private Integer age;
private String email;
private Long managerId;
private LocalDateTime createTime;
private LocalDateTime updateTime;
@Version
private Integer version;
private Integer deleted;
}
五. 性能分析插件
该插件会输出SQL语句的执行时间,以便做SQL语句的性能分析和调优。
注:3.2.0版本之后,mp自带的性能分析插件被官方移除了,而推荐食用第三方性能分析插件
(1)导入依赖: p6spy
(2) yml配置: spring: dataSource:
dirver-class-name: com.p6spy.engine.spy.p6spyDriver #换成p6spy的驱动
url: jdbc:p6spy:mysql://localhost:3306/db?serverTimezone=Asia/Shanghai
(3) 在src/main/resources资源目录下添加 spy.properties:
#spy.properties
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
# 真实JDBC driver , 多个以逗号分割,默认为空。由于上面设置了modulelist, 这里可以不用设置driverlist
#driverlist=com.mysql.cj.jdbc.Driver
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
#若要日志输出到文件, 把上面的appnder注释掉, 或者采用下面的appender, 再添加logfile配置
#不配置appender时, 默认是往文件进行输出的
#appender=com.p6spy.engine.spy.appender.FileLogger
#logfile=log.log
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,commit,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2
# 执行时间设置, 只有超过这个执行时间的才进行记录, 默认值0, 单位毫秒
executionThreshold=10