MyBatisPlus
0 快速开始
1.1 pom
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring-boot-starter-parent</artifactId> <groupId>org.springframework.boot</groupId> <version>2.3.4.RELEASE</version> </parent> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>mybatis-plusDemo</artifactId> <version>1.0-SNAPSHOT</version>
<dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> <scope>runtime</scope> </dependency> <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> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency>
</dependencies>
</project>1.2 application.yml
spring: datasource: url: jdbc:mysql://localhost:3306/mybatis-plus-demo username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver1.3 userMapper的编写
package com.xiaodidi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.xiaodidi.demo.User;import org.apache.ibatis.annotations.Mapper;
public interface UserMapper extends BaseMapper<User> {
}1.4 主程序
package com.xiaodidi;
import org.mybatis.spring.annotation.MapperScan;import org.mybatis.spring.annotation.MapperScans;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication@MapperScan("com.xiaodidi.mapper")public class MainApplication { public static void main(String[] args) { SpringApplication.run(MainApplication.class, args);
}
}1.5 测试
package com.xiaodidi;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.xiaodidi.demo.User;import com.xiaodidi.mapper.UserMapper;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;import java.util.List;@RunWith(SpringRunner.class)@SpringBootTestpublic class TestDemo { @Resource UserMapper userMapper; @Test public void save() { List<User> users = userMapper.selectList(null); for (User user : users) { System.out.println(user); } }}2.1 mybatis的主键(默认识别表中id字段为主键)
只要mybatis识别到主键,同时检测出来插入id为null,默认就自动使用了雪花算法,转换了然后在插入到数据库
有时数据库中的主键不是id也可以使用@TableId(“id”)指定主键
@TableId("id")//里面的id代表数据库中的字段private Long uid;
2.2 TableName
用来指定数据库中的名字,当数据库大的时候,通常使用t_user等标签来执行不同的服务,这是就需要通过TableName来执行表的名字,用来定位
2.3 @TableField
@TableField("name")name代表数据库中的字段,用来进行对应,不过mybatisplus默认把user_number 转换成userNumber,可以自动进行识别
2.4 myBatisPlus设置createTime
第一种方式
通过更改数据库中的

更改默认为当地的时间戳
(一般不用,不允许更改数据库)
第二种方式

@TableField(fill = FieldFill.INSERT)private Date createTime;
//更新时间@TableField(fill = FieldFill.INSERT_UPDATE)private Date updateTime;package com.atguigu.boot.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;import lombok.extern.slf4j.Slf4j;import org.apache.ibatis.reflection.MetaObject;import org.springframework.stereotype.Component;
import java.time.LocalDateTime;import java.util.Date;
@Slf4j@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler {
//插入时候的填充策略 @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill ...."); //日志 //设置字段的值(String fieldName字段名,Object fieldVal要传递的值,MetaObject metaObject) this.fillStrategy(metaObject, "createTime", new Date()); this.fillStrategy(metaObject, "updateTime", new Date());
//this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用) // this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`) /* 上面选其一使用,下面的已过时(注意 strictInsertFill 有多个方法,详细查看源码) */ //this.setFieldValByName("operator", "Jerry", metaObject); //this.setInsertFieldValByName("operator", "Jerry", metaObject); }
//更新时间的填充策略 @Override public void updateFill(MetaObject metaObject) { log.info("start update fill ...."); this.fillStrategy(metaObject, "updateTime", new Date());
//this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用) // this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug请升级到之后的版本如`3.3.1.8-SNAPSHOT`) /* 上面选其一使用,下面的已过时(注意 strictUpdateFill 有多个方法,详细查看源码) */ //this.setFieldValByName("operator", "Tom", metaObject); //this.setUpdateFieldValByName("operator", "Tom", metaObject); }}测试
@ResponseBody@RequestMapping("/saveRole")public String saveRole() { Role role = new Role(); role.setName("小"); roleService.save(role); return "成功";
}//自动会加入时间戳
注意:这里面所有的操作都需要使用mybatisBaseMapper里面自带的方法,因此不能使用自己写的方法使用createTime
2.5 乐观锁
面试中经常会问到乐观锁,悲观锁
乐观锁:顾名思义十分乐观,它总是被认为不会出现问题,无论干什么都不去上锁!如果出现了问题,再次更新测试
悲观锁:顾名思义十分悲观,它总是出现问题,无论干什么都会上锁!再去操作!
乐观锁实现方式
取出记录是,获取当前version 更新时,带上这个version 执行更新事,set version=newVersion where version =oldVersion 如果version不对,就更新失败 乐观锁: 1、先查询,获得版本号 version=1
--Aupdate user set name ="shuishui" ,version =version+1where id =2 and version=1
--B 如果线程抢先完成,这个时候version=2,会导致A修改失败update user set name ="shuishui" ,version =version+1where id =2 and version=1使用乐观锁
1 在数据库中加入version字段

2 在pojo类中加入version字段
@Versionprivate String version;3 加入配置
//开启事务@EnableTransactionManagement@Configurationpublic class MybatisPlusConfig {
@Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); }}2.6 查询操作
// 根据 ID 查询T selectById(Serializable id);// 根据 entity 条件,查询一条记录T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);// 根据 entity 条件,查询全部记录List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 查询(根据 columnMap 条件)List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);// 根据 Wrapper 条件,查询全部记录List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 entity 条件,查询全部记录(并翻页)IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录(并翻页)IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询总记录数Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);| 类型 | 参数名 | 描述 |
|---|---|---|
| Serializable | id | 主键ID |
| Wrapper | queryWrapper | 实体对象封装操作类(可以为 null) |
| Collection<? extends Serializable> | idList | 主键ID列表(不能为 null 以及 empty) |
| Map<String, Object> | columnMap | 表字段 map 对象 |
| IPage | page | 分页查询条件(可以为 RowBounds.DEFAULT) |
condition
@Testpublic void queryTest() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); //查询所有名字中存在o的,同时年龄在16到20中的,如果不写就不进行判断 String likeName = "o"; Integer maxAge = 20; Integer minAge = 16; //如果第一个参数返回的是true就加入,如果返回的false那么就不进行加入 userQueryWrapper.like(StringUtils.isNotBlank(likeName), "name", likeName) .gt(minAge!=null,"age",minAge) .lt(maxAge!=null,"age",maxAge); for (User user : userMapper.selectList(userQueryWrapper)) { System.out.println(user); }
}优先级
@Testpublic void queryTest2() { QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); //查润name中带o的,同时查找年龄大于16或者name=xiaodidi userQueryWrapper.like("name","o").and(userQueryWrapper1 -> userQueryWrapper1.gt("age",16).or().eq("name","xiaodidi")); List<User> users = userMapper.selectList(userQueryWrapper); for (User user : users) { System.out.println(user); }
}and中用消费型接口
LambdaQueryWrapper
@Testpublic void queryTest2() throws IOException { LambdaQueryWrapper<User> userQueryWrapper = new LambdaQueryWrapper<>(); //查润name中带o的,同时查找年龄大于16或者name=xiaodidi userQueryWrapper.like((User::getUsername), "o").and(userQueryWrapper1 -> userQueryWrapper1.gt(User::getAge,16).or().eq(User::getUsername,"xiaodidi")); List<User> users = userMapper.selectList(userQueryWrapper); for (User user : users) { System.out.println(user); }
}
模拟实现
package com.xiaodidi;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;import lombok.Data;import net.minidev.json.JSONUtil;
import java.lang.invoke.SerializedLambda;import java.lang.reflect.Method;
@Datapublic class LambdaTest {
private String fieldA;
public static void main(String[] args) throws Exception { SerializedLambda serializedLambda = doSFunction(LambdaTest::getFieldA); System.out.println("方法名:" + serializedLambda.getImplMethodName()); System.out.println("类名:" + serializedLambda.getImplClass());
}/*SerializedLambda对象了。 实际上序列化的对象是通过writeReplace方法产生的,那么我们要获取SerializedLambda对象没必要真的序列化和反序列化一遍。 反射调用writeReplace方法就可以了。*/ private static <T, R> java.lang.invoke.SerializedLambda doSFunction(SFunction<T, R> func) throws Exception { // 直接调用writeReplace Method writeReplace = func.getClass().getDeclaredMethod("writeReplace"); writeReplace.setAccessible(true); //反射调用 Object sl = writeReplace.invoke(func); java.lang.invoke.SerializedLambda serializedLambda = (java.lang.invoke.SerializedLambda) sl; return serializedLambda; }}2.7 myBatis-plus自带分页查询
放入分页的组件
@Beanpublic PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); //指定数据库类型,以防每次都要去适配数据库 paginationInterceptor.setDbType(DbType.MYSQL); return paginationInterceptor;}@ResponseBody@RequestMapping("/pageTest")public List<Role> pageTest() { Page<Role> rolePage = new Page<>(1, 5); roleMapper.selectPage(rolePage, null); return rolePage.getRecords();}自添加分页
IPage<User> getUsersgtAge(Page<User> page, @Param("age") Integer age);<select id="getUsersgtAge" resultMap="userResultMap"> select * from user where age > #{age}
</select>这样,也可以像别的分页一样进行操作
2.8 删除操作
// 根据 entity 条件,删除记录int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);// 删除(根据ID 批量删除)int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);// 根据 ID 删除int deleteById(Serializable id);// 根据 columnMap 条件,删除记录int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);| 类型 | 参数名 | 描述 |
|---|---|---|
| Wrapper | wrapper | 实体对象封装操作类(可以为 null) |
| Collection<? extends Serializable> | idList | 主键ID列表(不能为 null 以及 empty) |
| Serializable | id | 主键ID |
| Map<String, Object> | columnMap | 表字段 map 对象 |
2.9 配置逻辑删除
在表中添加一个字段

同时在类中也加入相同
private String deleted;mybatis3.3以前

myBatis 3.3 以后
global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0使用logic-delete-field
如果某个类全局逻辑指标要自己定义的时候添加
@TableLogicprivate String deleted;默认先从注解开始寻找,如果注解没有指定逻辑删除指标的时候,在从全局配置文件中寻找,如果都没有,那么就没有逻辑删除
注意:只针对myBatis-plus创建的sql有效,自己创建sql没有效果
2.10 代码自动生成器
代码自动生成器dao、pojo、conrtroller、service自动生成
AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、 Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。
package com.kuang;import com.baomidou.mybatisplus.annotation.DbType;import com.baomidou.mybatisplus.annotation.FieldFill;import com.baomidou.mybatisplus.annotation.IdType;import com.baomidou.mybatisplus.annotation.TableField;import com.baomidou.mybatisplus.generator.AutoGenerator;import com.baomidou.mybatisplus.generator.config.DataSourceConfig;import com.baomidou.mybatisplus.generator.config.GlobalConfig;import com.baomidou.mybatisplus.generator.config.PackageConfig;import com.baomidou.mybatisplus.generator.config.StrategyConfig;import com.baomidou.mybatisplus.generator.config.po.TableFill;import com.baomidou.mybatisplus.generator.config.rules.DateType;import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import java.util.ArrayList;// 代码自动生成器public class KuangCode { public static void main(String[] args) { // 需要构建一个 代码自动生成器 对象 AutoGenerator mpg = new AutoGenerator(); // 配置策略 // 1、全局配置 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.ID_WORKER); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true); mpg.setGlobalConfig(gc);
//2、设置数据源 DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/kuang_community? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("123456"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc);
//3、包的配置 PackageConfig pc = new PackageConfig(); pc.setModuleName("blog"); pc.setParent("com.kuang"); pc.setEntity("entity"); pc.setMapper("mapper"); pc.setService("service"); pc.setController("controller"); mpg.setPackageInfo(pc);
//4、策略配置 StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("blog_tags","course","links","sys_settings","user_record"," user_say"); // 设置要映射的表名 strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); // 自动lombok strategy.setEntityLombokModel(true); strategy.setLogicDeleteFieldName("deleted"); // 自动填充配置 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.setVersionFieldName("version"); strategy.setRestControllerStyle(true); strategy.setControllerMappingHyphenStyle(true); // localhost:8080/hello_id_2 mpg.setStrategy(strategy); mpg.execute(); //执行 }}
————————————————版权声明:本文为CSDN博主「?Handsome?」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/zdsg45/article/details/105138493/