mybaitsPlus使用笔记
mybaitsPlus入门
- 项目pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 映入plus后不用再引入mybaits,以避免因版本差异导致的问题。 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<!--mysql运行时依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
- sringboot配置文件
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456
- 测试demo代码
- mysql测试表
CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) ); <!-- 插入测试数据 --> INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com');
- 在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹
@SpringBootApplication @MapperScan("com.demo.mybatisPlus.mapper") public class MybatisPlusApplication { ...... }
- 创建实体类
@Data public class User { private Long id; private String name; private Integer age; private String email; }
- 创建包 mapper 编写Mapper 接口:UserMapper.java
// 一定要继承BaseMapper,泛型用对应的实体类 public interface UserMapper extends BaseMapper<User> { }
- 添加测试类,进行功能测试:
package com.demo.mybatisPlus; @SpringBootTest class MybatisPlusApplicationTests { @Autowired //如果userMapper有红色下划线(因为找不到注入的对象,因为类是动态创建的,但是程序可以正确的执行),所以可以在UserMapper接口处添加@Repository注解 private UserMapper userMapper; @Test void testSelectList() { //UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper //所以不填写就是无任何条件 List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } @Test public void testInsert(){ User user = new User(); user.setName("Helen"); user.setAge(18); user.setEmail("55317332@qq.com"); int result = userMapper.insert(user); System.out.println("影响的行数:" + result); //影响的行数 System.out.println("id:" + user); //id自动回填 } }
mybaitsPlus主键Id生成策略
//MyBatis-Plus默认的主键策略是:ASSIGN_ID (使用了雪花算法)
@TableId(type = IdType.ASSIGN_ID)
private String id;
//AUTO 自增策略
//需要在创建数据表的时候设置主键自增
//实体字段中配置
@TableId(type = IdType.AUTO)
private Long id;
//要想影响所有实体的配置,可以设置全局主键配置
//全局设置主键生成策略
mybatis-plus.global-config.db-config.id-type=auto
mybaitsPlus自动填充功能
- 数据库修改
在User表中添加datetime类型的新的字段 create_time、update_time - 实体类修改
实体上增加字段并添加自动填充注解
@Data
public class User {
......
@TableField(fill = FieldFill.INSERT)
private Date createTime;
//@TableField(fill = FieldFill.UPDATE)
// public enum FieldFill {
// /**
// * 默认不处理
// */
// DEFAULT,
// /**
// * 插入填充字段
// */
// INSERT,
// /**
// * 更新填充字段
// */
// UPDATE,
// /**
// * 插入和更新填充字段
// */
// INSERT_UPDATE
// }
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
- 实现元对象处理器接口
// 注意:不要忘记添加 @Component 注解
package com.demo.mybatisPlus.handler;
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.setFieldValByName("updateTime", new Date(), metaObject);
}
}
mybaitsPlus悲观锁和乐观锁
-
悲观锁:悲观锁是指操作数据的时候没有安全感,总是会认为自己在拿走数据后别人正好也要操作这条数据,所以悲观锁每次操作数据都会加一个锁,这样别人想拿这个数据就会阻塞直到他也能拿到锁(而共享资源每次只给一个线程使用,其他线程互斥访问)
-
乐观锁:操作数据时总是假设最好的情况,每次去拿数据的时候都想当然的认为别人不会修改,也就不会上锁,但是在更新数据的时候会比较一下在此期间别人有没有去更新这个数据,可以使用版本号机制和compareAndSet算法实现。乐观锁适用于多读的应用类型,这样不会频繁的更改数据。也就不会频散的加锁,这样可以提高吞吐量,
- 乐观锁实现流程
- 修改实体类
//添加 @Version 注解 //数据库表中也要有该字段 @Version private Integer version;
- 创建配置文件
//创建包config,创建文件MybatisPlusConfig.java
//此时可以删除主类中的 @MapperScan 扫描注解
package com.demo.mybatisPlus.config;
@EnableTransactionManagement
@Configuration
@MapperScan("com.demo.mybatisPlus.mapper")
public class MybatisPlusConfig {
}
//注册乐观锁插件
//在 MybatisPlusConfig 中注册 Bean
/**
* 乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
mybaitsPlus查询和分页
- 通过多个id批量查询
//完成了动态sql的foreach的功能
@Test
public void testSelectBatchIds(){
//1.直接分页
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
//2.通过前端传入参数分页
Page<AdType> pageParam = new Page<>(page, limit);
IPage<AdType> pageModel = adTypeService.page(pageParam);
List<AdType> records = pageModel.getRecords();
long total = pageModel.getTotal();
return R.ok().data("total", total).data("rows", records);
//3.自定义SQL分页
IPage<AdVo> pageModel = adService.selectPage(page, limit);
List<AdVo> records = pageModel.getRecords();
long total = pageModel.getTotal();
return R.ok().data("total", total).data("rows", records);
//service实现层
@Override
public IPage<AdVo> selectPage(Long page, Long limit) {
QueryWrapper<AdVo> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByAsc("a.type_id", "a.sort");
Page<AdVo> pageParam = new Page<>(page, limit);
//自定义SQL
List<AdVo> records = baseMapper.selectPageByQueryWrapper(pageParam, queryWrapper);
pageParam.setRecords(records);
return pageParam;
}
//DAO层
<select id="selectPageByQueryWrapper" resultType="com.atguigu.guli.service.cms.entity.vo.AdVo">
SELECT
a.id,
a.title,
a.sort,
t.title AS type
FROM cms_ad a
LEFT JOIN cms_ad_type t ON a.type_id = t.id
${ew.customSqlSegment}
</select>
}
}
- 简单的条件查询
//通过map封装查询条件
//注意:map中的key对应数据库中的列名。如:数据库user_id,实体类是userId,这时map的key需要填写user_id
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name", "Helen");
map.put("age", 18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
-
分页
- 分页插件,MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
- 添加分页插件
配置类中添加@Bean配置 /** * 分页插件 */ @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }
- 测试selectPage分页
//测试:最终通过page对象获取相关数据 @Test public void testSelectPage() { Page<User> page = new Page<>(1,5); Page<User> pageParam = userMapper.selectPage(page, null); pageParam.getRecords().forEach(System.out::println); System.out.println(pageParam.getCurrent()); System.out.println(pageParam.getPages()); System.out.println(pageParam.getSize()); System.out.println(pageParam.getTotal()); System.out.println(pageParam.hasNext()); System.out.println(pageParam.hasPrevious()); } //控制台sql语句打印:SELECT id,name,age,email,create_time,update_time FROM user LIMIT 0,5
- 分页插件,MyBatis Plus自带分页插件,只要简单的配置即可实现分页功能
-
返回指定的列,
- 当指定了特定的查询列时,希望分页结果列表只返回被查询的列,而不是很多null值
//测试selectMapsPage分页:结果集是Map @Test public void testSelectMapsPage() { //返回很多null列 //Page<User> page = new Page<>(1, 5); //QueryWrapper<User> queryWrapper = new QueryWrapper<>(); //queryWrapper.select("name", "age"); //Page<User> pageParam = userMapper.selectPage(page, queryWrapper); // //pageParam.getRecords().forEach(System.out::println); //Page不需要泛型 Page<Map<String, Object>> page = new Page<>(1, 5); QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.select("name", "age"); Page<Map<String, Object>> pageParam = userMapper.selectMapsPage(page, queryWrapper); List<Map<String, Object>> records = pageParam.getRecords(); records.forEach(System.out::println); System.out.println(pageParam.getCurrent()); System.out.println(pageParam.getPages()); System.out.println(pageParam.getSize()); System.out.println(pageParam.getTotal()); System.out.println(pageParam.hasNext()); System.out.println(pageParam.hasPrevious()); }
mybaitsPlus删除
-
删除
- 根据id删除记录
@Test public void testDeleteById(){ int result = userMapper.deleteById(5L); System.out.println(result); }
- 批量删除
@Test public void testDeleteBatchIds() { int result = userMapper.deleteBatchIds(Arrays.asList(8, 9, 10)); System.out.println(result); }
- 简单条件删除
@Test public void testDeleteByMap() { HashMap<String, Object> map = new HashMap<>(); map.put("name", "Helen"); map.put("age", 18); int result = userMapper.deleteByMap(map); System.out.println(result); }
-
逻辑删除
- 物理删除和逻辑删除,物理删除:真实删除,将对应数据从数据库中删除,之后查询不到此条被删除数据逻辑删除:假删除,将对应数据中代表是否被删除字段状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录
- 逻辑删除实现流程
- 数据库修改
添加 deleted字段 ALTER TABLE `user` ADD COLUMN `deleted` boolean DEFAULT false
- 实体类修改
添加deleted 字段,并加上 @TableLogic 注解 @TableLogic private Integer deleted;
- 配置(可选)
application.properties 加入以下配置,此为默认值,如果你的默认值和mp默认的一样,该配置可无 mybatis-plus.global-config.db-config.logic-delete-value=1 mybatis-plus.global-config.db-config.logic-not-delete-value=0
- 测试
//测试后发现,数据并没有被删除,deleted字段的值由0变成了1 //测试后分析打印的sql语句,是一条update //注意:被删除前,数据的deleted 字段的值必须是 0,才能被选取出来执行逻辑删除的操作 @Test public void testLogicDelete() { int result = userMapper.deleteById(1L); System.out.println(result); }
- 测试逻辑删除后的查询
//MyBatis Plus中查询操作也会自动添加逻辑删除字段的判断 @Test public void testLogicDeleteSelect() { List<User> users = userMapper.selectList(null); users.forEach(System.out::println); }
mybaitsPlus常用构造器
@SpringBootTest
public class QueryWrapperTests {
@Autowired
private UserMapper userMapper;
}
-
测试用例
- 1、ge、gt、le、lt、isNull、isNotNull
@Test public void testDelete() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper .isNull("name") .ge("age", 12) .isNotNull("email"); int result = userMapper.delete(queryWrapper); System.out.println("delete return count = " + result); }
- eq、ne
//注意:seletOne()返回的是一条实体记录,当出现多条时会报错 @Test public void testSelectOne() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("name", "Tom"); User user = userMapper.selectOne(queryWrapper);//只能返回一条记录,多余一条则抛出异常 System.out.println(user); }
- between、notBetween
//包含大小边界 @Test public void testSelectCount() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.between("age", 20, 30); Integer count = userMapper.selectCount(queryWrapper); //返回数据数量 System.out.println(count); }
- like、notLike、likeLeft、likeRight
//selectMaps()返回Map集合列表,通常配合select()使用 @Test public void testSelectMaps() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper .select("name", "age") .like("name", "e") .likeRight("email", "5"); List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);//返回值是Map列表 maps.forEach(System.out::println); }
- in、notIn、inSql、notinSql、exists、notExists
// in、notIn: //notIn("age",{1,2,3})--->age not in (1,2,3) //notIn("age", 1, 2, 3)--->age not in (1,2,3) //inSql、notinSql:可以实现子查询 //例: inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6) //例: inSql("id", "select id from table where id < 3")--->id in (select id from table where id < @Test public void testSelectObjs() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.in("id", 1, 2, 3); queryWrapper.inSql("id", "select id from user where id <= 3"); List<Object> objects = userMapper.selectObjs(queryWrapper);//返回值是Object列表 objects.forEach(System.out::println); }
- or、and
// 注意:这里使用的是 UpdateWrapper //不调用or则默认为使用 and 连 @Test public void testUpdate1() { //修改值 User user = new User(); user.setAge(99); user.setName("Andy"); //修改条件 UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>(); userUpdateWrapper .like("name", "h") .or() .between("age", 20, 30); int result = userMapper.update(user, userUpdateWrapper); System.out.println(result); }
- lambda表达式
//lambda表达式内的逻辑优先运算 @Test public void testUpdate2() { //修改值 User user = new User(); user.setAge(99); user.setName("Andy"); //修改条件 UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>(); userUpdateWrapper .like("name", "n") .or(i -> i.like("name", "a").eq("age", 20)); int result = userMapper.update(user, userUpdateWrapper); System.out.println(result); }
- orderBy、orderByDesc、orderByAsc
@Test public void testSelectListOrderBy() { QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.orderByDesc("age", "id"); List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
- set、setSql
//最终的sql会合并 user.setAge(),以及 userUpdateWrapper.set() 和 setSql() 中 的字段 @Test public void testUpdateSet() { //修改值 User user = new User(); user.setAge(60); //修改条件 UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>(); userUpdateWrapper .like("name", "h") .set("name", "Peter")//除了可以查询还可以使用set设置修改的字段 .setSql(" email = '123@qq.com'");//可以有子查询 int result = userMapper.update(user, userUpdateWrapper); }
-
setSqlSelect
- 设置 SELECT 查询字段
-
where
- WHERE 语句,拼接 + WHERE 条件
-
and
- AND 语句,拼接 + AND 字段=值
-
andNew
- AND 语句,拼接 + AND (字段=值)
-
or
- OR 语句,拼接 + OR 字段=值
-
orNew
- OR 语句,拼接 + OR (字段=值)
-
eq
- 等于=
-
allEq
- 基于 map 内容等于=
-
ne
- 不等于<>
-
gt
- 大于>
-
ge
- 大于等于>=
-
lt
- 小于
-
le
- 小于等于<=
-
like
- 模糊查询 LIKE
-
notLike
- 模糊查询 NOT LIKE
-
in
- IN 查询
-
notIn
- NOT IN 查询
-
isNull
- NULL 值查询
-
isNotNull
- IS NOT NULL
-
groupBy
- 分组 GROUP BY
-
having
- HAVING 关键词
-
orderBy
- 排序 ORDER BY
-
orderAsc
- ASC 排序 ORDER BY
-
orderDesc
- DESC 排序 ORDER BY
-
exists
- EXISTS 条件语句
-
notExists
- NOT EXISTS 条件语句
-
between
- BETWEEN 条件语句
-
notBetween
- NOT BETWEEN 条件语句
-
addFilter
- 自由拼接 SQL
-
last
- 拼接在最后,例如:last(“LIMIT 1”)
mybaitsPlus代码生成器模板
- 在test测试文件夹使用模板
//数据库表的命名:“业务名称_表的作用”。如,edu_teacher package com.demo.mybaitsPlus.service.edu; public class CodeGenerator { @Test public void genCode() { String prefix = "dbxxx_"; String moduleName = "edu"; // 1、创建代码生成器 AutoGenerator mpg = new AutoGenerator(); // 2、全局配置 GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("");//设置作者 gc.setOpen(false); //生成后是否打开资源管理器 // gc.setFileOverride(false); //重新生成时文件是否覆盖 gc.setServiceName("%sService"); //去掉Service接口的首字母I gc.setIdType(IdType.ASSIGN_ID); //主键策略 gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型 gc.setSwagger2(true);//开启Swagger2模式 mpg.setGlobalConfig(gc); // 3、数据源配置 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/" + "tablename" + "?useUnicode=true&characterEncoding=utf8zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("123456"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 4、包配置 PackageConfig pc = new PackageConfig(); pc.setModuleName(moduleName); //模块名 pc.setParent("com.demo.mybaitsPlus.service"); pc.setController("controller"); pc.setEntity("entity"); pc.setService("service"); pc.setMapper("mapper"); mpg.setPackageInfo(pc); // 5、策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略 strategy.setTablePrefix(moduleName + "_");//设置表前缀不生成 strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略 strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作 strategy.setLogicDeleteFieldName("is_deleted");//逻辑删除字段名 strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀 //自动填充 TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT); TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(gmtCreate); tableFills.add(gmtModified); strategy.setTableFillList(tableFills); strategy.setRestControllerStyle(true); //restful api风格控制器 strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符 mpg.setStrategy(strategy); // 6、执行 mpg.execute(); } }
- 优化代码生成器
- 创建BaseEntity
- 在service_base中创建BaseEntity实体类
package com.atguigu.guli.service.base.model; @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) public class BaseEntity implements Serializable { private static final long serialVersionUID=1L; @ApiModelProperty(value = "ID") @TableId(value = "id", type = IdType.ASSIGN_ID) private String id; @ApiModelProperty(value = "创建时间") @TableField(fill = FieldFill.INSERT) private Date Create; @ApiModelProperty(value = "更新时间") @TableField(fill = FieldFill.INSERT_UPDATE) private Date Modified; }
- 设置SupterClass
//设置BaseEntity strategy.setSuperEntityClass("com.demo.mybaitsPlus.service.base.model.BaseEntity"); // 填写BaseEntity中的公共字段 strategy.setSuperEntityColumns("id", "demo_create", "demo_modified");//数据库中对应字段
- 重新执行代码生成器
所有实体类都继承了BaseEntity
- 在service_base中创建BaseEntity实体类
- 创建BaseEntity