mybatis-plus
mybatis-plus开发学习
1 mybatis-plus开发文档
https://baomidou.com/guides/wrapper/#like
2 mybatis-plus配置
2.1 导pom
1.只用mybais-plus实现单表的增删改查,只要在pom种引入mybatis-plus-boot-starter起步依赖就行。
2.如果要自动生成代码
-
引入mp起步依赖
-
引入mybatis-plus-generate依赖和freemarker或者其他模板引擎依赖。
-
编写CodeGenerator这个配置类,具体参考
-
MyBatis-plus 代码生成器(全程保姆式教程)_mybatis-plus-generator 3.5.6-CSDN博客
3.使用mybatis-plus插件图形化生成。不过要引入swagger的依赖。
2.2 yml配置
mybatis-plus:
# xml
mapper-locations: classpath:mapper/*Mapper.xml
# 实体扫描,多个package用逗号或者分号分隔
type-aliases-package: com.jiagoushi.mybatisplusexample.entity
# 扫描枚举类 # 支持统配符 * 或者 ; 分割
type-enums-package: com.jiagoushi.mybatisplusexample.entity.enums
global-config:
db-config:
#数据库大写下划线转换,避免字段是大写,实体类是小写的不匹配问题
capital-mode: true
#逻辑删除配置
# logic-delete-field: delete_status # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑删除
logic-not-delete-value: 0 # 正常
configuration:
###自动下划线转驼峰,默认开启,此处显式配置了一下
map-underscore-to-camel-case: true
cache-enabled: false
default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler
#################################### logic-delete-value: 1 # 逻辑删除
logic-not-delete-value: 0 # 正常
这两个配置牵扯到逻辑删除和物理删除的问题,
- 物理删除就是我使用delete语句实现的删除。
- 逻辑删除是删除的时候使用update语句修改del_flag删除标记的值,只是逻辑上删除了,但是数据库里面还是有的。
- mybatis-plus配置完之后,执行相应的查询和修改语句时候,mybatis-plus自动给我添加标志位的字段。
逻辑标志位要加这个@TableLogic注解
######################################
3 mp的使用学习
3.1 雪花算法
实体类里面用@TableId里面有一个属性是type,用mp的枚举类型利用雪花算法来生成主键,不用在数据库里面手动设置自增了。
3.2 乐观锁
3.2.1 学习乐观锁之前,复习一下并发的概念
并发是怎么回事?
- 单核 CPU:多个任务通过时间片轮转交替执行,看起来像是同时进行,但实际上是在不同时刻执行的。
- 多核 CPU:多个任务可以真正在同一时刻执行。
并发控制又是怎么回事?
3.2.2 乐观锁配置
1.建插件配置类
@Configuration
@MapperScan("com.jiagoushi.mybatisplusexample.mapper")
public class MyBatisPlusConfiguration {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
/*MybatisPlusInterceptor是mp内置核心拦截器,用于内部的插件管理*/
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
/*在这个插件管理对象中分别添加分页插件和乐观锁插件*/
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); // 分页插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件
return interceptor;
}
}
2.在实体类字段version上加上@Version
乐观锁生效是,每修改一次数据,version自动加一,但是修改方法在service层执行前要先调一次查询,获得version,然后再修改。
补一下我的困惑
3.3 为什么继承的ServiceImpl泛型第一个参数必须是自定义的接口
- Spring 的依赖注入是基于 Bean 的类型 而非泛型参数。如果存在多个
BaseMapper
的实例(如BaseMapper<User>
和BaseMapper<Order>
),Spring 无法区分它们,导致注入冲突。
不过在mapper层继承basemapper的基础下,service层继承serviceimpl可以不写参数类型。spring和mp会自动根据自定义的mapper来进行映射,前提是继承basemapper,但是如果是多个service都继承但是不写泛型,会导致每个service都可以参与到其他实体类的crud中,直接混乱了。
- 使用serviceImpl一定要有mapper层接口继承basemapper,因为service调用的默认的crud是basemapper的实现类去执行的,而mp无法生成泛型接口的动态实现类,机制限制:MyBatis-Plus 内部机制的限制:MyBatis-Plus 没有直接对泛型接口生成实现类的机制,因为动态代理无法处理泛型接口
- 继承类basemapper一定要在serviceImpl里面写泛型参数,否则mp不知道service控制的哪个实体类。
3.4 mybatis和mybatis-plus都怎样识别表名
mybatis通过xml中的sql语句的开发者定义的表名映射。
mybatis-plus
- 如果表名和实体类名一样,则不用配置自动生成的sql语句以user为表名
- 如果表名有前缀,就要配置了,自己无法发现,一种是使用@TableName注解,一种是yml里面全局配置前缀。不过你要不用他简单的,自己写xml不用配置也行。
4 springboot整合mybatisplus开发笔记
配合springboot_rbac和springboot_mybatisplus连个demo学习
1.导pom
<!-- mybatis-plus起步依赖插件 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<!--mp自动生成代码,api文档注释需要swagger注解需要的依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger2.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger2.version}</version>
</dependency>
2.配置yml文件(#格线是mp的配置)
# tomcat服务器端口配置
server:
port: 8080
servlet:
context-path: /rbac
# spring数据源配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/rbac?serverTimezone=Asia/Shanghai&useTimezone=true/&characterEncoding=utf8
username: root
password: Yin2040411
max-wait: 10000
initial-size: 5
# 配置jackson的日期时间格式
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
#####################################################################################################
# mybatis-plus框架配置
mybatis-plus:
# 配置映射文件和别名
mapper-locations: classpath*:/com/yzh/springboot_rbac/mapper/*.xml
type-aliases-package: com.yzh.springboot_rbac.entity
# mp逻辑删除配置:
global-config:
db-config:
logic-delete-field: del_flag
logic-delete-value: 0 #逻辑删除
logic-not-delete-value: 1 #正常
# 数据库字段大写转小写配置
capital-mode: true
configuration:
# 下划线自动转托驼峰配置(不配置也是开启的,这里显式配置一下啦)
map-underscore-to-camel-case: true
# 让sql语句日志打印到控制台配置
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 控制台打印日志的详细程度配置
# trace:最详细日志信息
logging:
level:
root: info # 默认就是info
com.yzh.springboot_rbac.service.impl: trace
com.yzh.springboot_rbac.controller: trace
#日志生成路径配置
logback:
logDir: log/dev
appName: springboot_rbac
fileType: log
#######################################################################################
logback-spring.xml要注意一下该环境否则启动后一直卡在那里
logback-spring.xml里面这地方复制过来要改一下环境默认的default,这里需要改为default环境,
要不然项目一直卡在哪里启动不了,因为我没有配置dev的yml文件,所以只能使用default的文件。
<!--开发环境:打印控制台-->
<!--这里需要改为default环境,要不然项目一直卡在哪里启动不了,因为我没有配置dev的yml文件,所以只能使用
default的文件-->
<springProfile name="default">
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
3.
在springboot_mybatisplus项目中练习mp提供的方法记录
###################################################
如果想要在测试类中链式调用set方法设置对象的值,一定要把实体类里面的对应
set方法返回类型改为实体类形,retrun this
public User setName(String name) {
this.name = name;
return this;
}
public User setAge(Integer age) {
this.age = age;
return this;
}
public User setBir(Date bir) {
this.bir = bir;
return this;
}
public void testInsert() {
User user = new User();
user.setAge(20).setName("张三").setBir(new Date());
userMapper.insert(user);
}
#############################################################
4.
springboot中自定义测试类实现测试两种方法
- 1.测试类要加@SpringBootTest这个注解,springboot项目测试类要加这个注解,
否则无法测试,因为springboot项目需要依赖springboot来管理bean,否则usermapper无法注入。
- 2.自定义测试类继承默认生成的测试类,因为默认生成的测试类里面使用了这个注解
5.
雪花算法的:ASSIGN_ID实体类使用Long/String,数据库字段使用bigInt/varchar
ASSIGN_UUID实体类使用String,数据库字段使用Varchar
按照上述即可,实体类Integer和int数值范围对应数据库int范围太小,报错。
ASSIGN_ID设置后,mp自动生成1911670271488589825并插入到数据库中,数据库不用设置自增
ASSIGN_UUID:d7d3d88d136905d4362bcb7e1cef04cf,数据库不用设置自增
AUTO:需要依赖数据库设置自增才可以添加id,否则报错。
6.lambda表达式和引用方式如下:效果一样
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
users.forEach(user -> System.out.println(user));
7.常用的方法与条件构造器
@SpringBootTest
public class testBaseController {
/*此测试对自定义mapper继承BaseController测试*/
@Autowired
private UserMapper userMapper;
@Test
public void testSelectAllUser() {
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
users.forEach(user -> System.out.println(user));
}
@Test
public void testSelectByCondition() {
/*查询常用的有两个:
* selectById:根据id查,参数是id的值
* selectList:查询所有用户,null空表示无条件所有用户*/
// User user = userMapper.selectById(1);
// System.out.println(user);
/*基于条件的查询*/
// QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.like("name", "李");
// List<User> list = userMapper.selectList(queryWrapper);
// list.forEach(user -> System.out.println(user));
/*lambda的查询*/
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(true, User::getName, "多");
List<User> list = userMapper.selectList(lambdaQueryWrapper);
list.forEach(user -> System.out.println(user));
}
@Test
public void testInsert() {
User user = new User();
user.setAge(20).setName("李四").setBir(new Date());
userMapper.insert(user);
/*insert(T entity):插入方法只有一个,参数为实体类*/
}
@Test
public void testdelete() {
/*删除常用的有两个:
* deleteById:genjuid删除,参数是id的值
* delete:根据条件删除,参数是条件构造器*/
/*普通删除*/
// userMapper.deleteById(2);
/*基于条件修改
* querywrapper形式*/
// QueryWrapper<User> queryWrapper = new QueryWrapper<>();
// queryWrapper.eq(true, "id", 3);
// userMapper.delete(queryWrapper);
/*lambdaqueryWrapper形式*/
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(true, User::getId, 4);
userMapper.delete(lambdaQueryWrapper);
}
@Test
public void testUpdate1() {
/*updateById():参数是实体对象,基于id修改:先查询,把查询的对象赋值给user,然后修改*/
User user = userMapper.selectById(1);
user.setAge(10).setName("伍六七");
userMapper.updateById(user);
}
@Test
public void testUpdate2() {
/*基于条件修改:
update()参数是修改后的实体类和条件构造器里面的条件
* QueryWrapper相当于sql中的where表达式eq相当于where age = 10*/
User user = userMapper.selectById("1");
user.setName("多阔霍");
// QueryWrapper<User> updateWrapper = new QueryWrapper<>();
// updateWrapper.eq("age", 10);
// userMapper.update(user, updateWrapper);
/*写一个lambdaWrapper,这个是使用lambda的方法引用实现正向映射数据库字段的值
* 注意,这里的方法引用不是lambda里面的获取的值,而是只获取方法名,即属性名
* 方法的引用根据应用上下文来执行,lambda中方法引用会输出一个值,但是mp中输出列名*/
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(true, User::getAge, 10);
userMapper.update(user, lambdaQueryWrapper);
}
}
8.
注意:
- Iservice使用的话要和BaseMapper一起使用,所以一定要自定义一个具体的接口继承BaseMapper,
如果直接施一公Iservice,spring无法注入泛型接口BaseMapper,必须指定具体实例接口
- 但是可以不适用Iservice,只用BaseMapper的crud即可,Iservice提供了更复杂的方法,而且不用自己写方法在service层。
作者:yzh先生
博客:https://www.cnblogs.com/ZiJun
本文版权归作者和博客园共有,转载请在文章中注明原文链接:https://www.cnblogs.com/ZiJun/p/18852473 ,愿尊重劳动成果,谢谢!
若有关于博客内容的各种问题,欢迎在评论区讨论或发消息,让我们一起进步!