08-常见需求

nobility 发布于 2022-05-21 987 次阅读


常见需求

逻辑删除

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据,且使用wrapper.entity生成的where条件会忽略该字段
  • 更新: 追加where条件防止更新到已删除数据,且使用wrapper.entity生成的where条件会忽略该字段
  • 删除: 转变为更新

注意事项

  • 自定义SQL不会添加逻辑删除功能,可通过自己在SQL中添加该条件或在Wrapper中添加该条件来解决
  • 在查询时,也会默认查询出删除标记字段,可通过实体上的该标记属性添加@TableField(select = false)注解来解决
  1. 添加逻辑删除配置
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  # 全局逻辑删除的实体字段名
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  1. 如果是3.1.1之前版本,需要在配置类中注册sql注入器
@Configuration
public class MybatisPlusConfig {
    @Bean
    public ISqlInjector SqlInjector() {
        return new LogicSqlInjector();
    }
}
  1. 如果是3.3.0之前版本,需要在实体类的删除标记字段上,加上@TableLogic注解
@TableLogic
private Integer deleted;

自动填充

  1. 使用@TableField注解,修改注解的fill属性为FieldFill枚举类的属性
枚举类型 描述
DEFAULT 默认不处理
INSERT 插入填充字段
UPDATE 更新填充字段
INSERT_UPDATE 插入和更新填充字段
  1. 定义处理器类,实现MyMetaObjectHandler接口
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {  //插入填充
        String fieldName = "createTime";
        if (!metaObject.hasSetter(fieldName)) {  //如果该实体没有要填充的字段则跳过
            return;
        }
        if (this.getFieldValByName(fieldName,metaObject) != null) {  //如果实体实体已经自定义了该字段的值则跳过
            return;
        }
        this.setFieldValByName(fieldName, LocalDateTime.now(), metaObject);  //参数依次是:要填充的字段名、要填充的内容、元数据对象
    }

    @Override
    public void updateFill(MetaObject metaObject) {  //更新填充
        String fieldName = "updateTime";
        if (!metaObject.hasSetter(fieldName)) {  //如果该实体没有要填充的字段则跳过
            return;
        }
        if (this.getFieldValByName(fieldName,metaObject) != null) {  //如果实体实体已经自定义了该字段的值则跳过
            return;
        }
        this.setFieldValByName(fieldName, LocalDateTime.now(), metaObject);  //参数依次是:要填充的字段名、要填充的内容、元数据对象
    }
}

乐观锁

注意事项

  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下newVersion = oldVersion + 1,并且会回写到entity
  • 仅支持 updateById(id)update(entity, wrapper) 方法,并且在update(entity, wrapper)方法下,wrapper不能复用,否则插件会拼接两个version条件字段
  1. 配置乐观锁插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  //创建MybatisPlus拦截器
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());  //添加乐观锁插件
    return interceptor;
}
  1. 在实体类的version字段上加上@Version注解
@Version
private Integer version;

性能分析

  1. 将下面的依赖代码加入pom.xml文件中
<dependency>
  <groupId>p6spy</groupId>
  <artifactId>p6spy</artifactId>
  <version>x.x.x</version>
</dependency>
  1. 将application配置文件中数据库驱动和数据库连接URL修改为p6spy的格式
spring.datasource.driverClassName=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:mysql://IP地址:端口/数据库?参数
  1. 在application配置文件同级目录下创建spy.properties文件,进行下面配置
# 指定应用的日志拦截模块,默认为com.p6spy.engine.spy.P6SpyFactory
#3.2.1以上使用
modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
#3.2.1以下使用或者不配置
#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory

# 真实JDBC driver , 多个以 逗号 分割 默认为空
driverlist=com.mysql.cj.jdbc.Driver

# 配置SimpleDateFormat日期格式 默认为空
dateformat=yyyy-MM-dd HH:mm:ss

# 控制台打印
appender=com.p6spy.engine.spy.appender.StdoutLogger
# 日志系统打印
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 文件打印
#appender=com.p6spy.engine.spy.appender.FileLogger
# 指定 Log 的文件名 默认 spy.log
#logfile=spy.log
# 指定是否每次是增加 Log,设置为 false 则每次都会先进行清空 默认true
#append=true

# 指定日志输出样式  默认为com.p6spy.engine.spy.appender.SingleLineFormat , 单行输出 不格式化语句
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
# 也可以采用  com.p6spy.engine.spy.appender.CustomLineFormat 来自定义输出样式, 默认值是%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)
# 可用的变量为:
#   %(connectionId)            connection id
#   %(currentTime)             当前时间
#   %(executionTime)           执行耗时
#   %(category)                执行分组
#   %(effectiveSql)            提交的SQL 换行
#   %(effectiveSqlSingleLine)  提交的SQL 不换行显示
#   %(sql)                     执行的真实SQL语句,已替换占位
#   %(sqlSingleLine)           执行的真实SQL语句,已替换占位 不换行显示
#customLogMessageFormat=%(currentTime)|%(executionTime)|%(category)|connection%(connectionId)|%(sqlSingleLine)

# 设置 p6spy driver 代理
deregisterdrivers=true

# 取消JDBC URL前缀
useprefix=true

# date类型字段记录日志时使用的日期格式 默认dd-MMM-yy
databaseDialectDateFormat=dd-MMM-yy

# boolean类型字段记录日志时使用的日期格式 默认boolean 可选值numeric
databaseDialectBooleanFormat=boolean

#显示指定过滤 Log 时排队的分类列表,取值:
#error, info, batch, debug, statement, commit, rollback, result and resultset are valid values
#(默认 info,debug,result,resultset,batch)
excludecategories=info,debug,result,resultset,batch

# P6Outage 模块是否记录较长时间运行的语句 默认false
outagedetection=true
# P6Outage 模块执行时间设置,整数值 (以秒为单位)),只有当超过这个时间才进行记录 Log。 默认30s
outagedetectioninterval=2

动态表名

  1. 配置动态表名插件
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  //创建MybatisPlus拦截器
        DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();  //创建动态表名解析器
        HashMap<String, TableNameHandler> map = new HashMap<>();  //用于存储表名和表名处理器的映射关系
        map.put("user", (sql, tableName) -> {  //这里的表名是数据库中的表名而并非实体名
            String suffix = "_01";
            //此处可对表后缀增加一些逻辑
            return tableName + suffix;  //原来表名拼接一个经过逻辑处理后的后缀,如果返回null则不替换
        });
        dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);  //将表名和动态表名映射关系设置
        interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);  //添加动态表名解析器
        return interceptor;
    }
}
  1. 之后在对原表名进行处理时,会先对表名进行动态处理,之后才会执行SQL语句

代码生成器

  1. 引入代码生成器依赖和模版引擎依赖
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>x.x.x</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>x.x.x</version>
</dependency>
  1. 编写代码,配置代码生成器
class CodeGenerator {
    
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ":");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator autoGenerator = new AutoGenerator();

        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");  //当前运行路径
        globalConfig.setOutputDir(projectPath + "/src/main/java");  //输出目录
        globalConfig.setAuthor("作者");  //作者
        globalConfig.setOpen(false);  //生成完后是否打开资源管理器
        globalConfig.setServiceName("%sService");  //去掉Service杰昆的首字母I
        globalConfig.setFileOverride(false);  //重新生死文件时文件是否覆盖
        globalConfig.setIdType(IdType.ASSIGN_ID);  //主键生成策略
        globalConfig.setDateType(DateType.ONLY_DATE);  //定义生成实体类中的日期类型
        globalConfig.setSwagger2(true); //实体属性使用Swagger2注解
        autoGenerator.setGlobalConfig(globalConfig);

        // 数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setDbType(DbType.MYSQL);
        dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/ant?useUnicode=true&useSSL=false&characterEncoding=utf8");
        dataSourceConfig.setSchemaName("public");
        dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("密码");
        autoGenerator.setDataSource(dataSourceConfig);

        // 包配置
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent(scanner("父包名"));  //父包名
        packageConfig.setModuleName(scanner("当前模块名")); //当前模块名
        packageConfig.setController("controller");
        packageConfig.setEntity("entity");
        packageConfig.setService("service");
        packageConfig.setMapper("mapper");
        autoGenerator.setPackageInfo(packageConfig);
      
        // 模版引擎配置
        autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());
      
        // 策略配置
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setNaming(NamingStrategy.underline_to_camel); //数据库表名映射到实体的命名策略
        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); //数据库表列名映射到实体的命名策略
        strategyConfig.setRestControllerStyle(true); //restful api风格控制器
        strategyConfig.setEntityLombokModel(true);  // lombok 模型 @Accessors(chain = true) setter链式操作
        //strategyConfig.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
        //strategyConfig.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
        strategyConfig.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategyConfig.setControllerMappingHyphenStyle(true);  //url中驼峰转连字符
        strategyConfig.setTablePrefix(packageConfig.getModuleName() + "_");  //生成实体时去掉表前缀
        autoGenerator.setStrategy(strategyConfig);

        autoGenerator.execute();
      
      	System.exit(0);
    }
}
此作者没有提供个人介绍
最后更新于 2022-05-21