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.Driver
1.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)
@SpringBootTest
public 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
@Component
public 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
--A
update user set name ="shuishui" ,version =version+1
where id =2 and version=1
--B 如果线程抢先完成,这个时候version=2,会导致A修改失败
update user set name ="shuishui" ,version =version+1
where id =2 and version=1
使用乐观锁
1 在数据库中加入version字段
2 在pojo类中加入version字段
@Version
private String version;
3 加入配置
//开启事务
@EnableTransactionManagement
@Configuration
public 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);
condition
@Test
public 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);
}
}
优先级
@Test
public 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
@Test
public 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;
@Data
public 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自带分页查询
放入分页的组件
@Bean
public 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);
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:deleted 来指定全局的逻辑删除指标 相当于deleted
如果某个类全局逻辑指标要自己定义的时候添加
@TableLogic
private 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/