芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作

SpringBoot下使用mybatis-plus的mysql操作

  • 一、目标
    • 1.1 快速开发更有意义
    • 1.2 拥抱数据库表的变化
    • 1.3 大胆使用枚举
    • 1.4 放心去删除
    • 1.5 基本的存证记录
    • 1.6 版本-基本的防并发冲突处理
  • 二、制定代码编写规范与思考
    • 2.1 类与字段命名
    • 2.2 mybatis-plus生成service与正常service
    • 2.3 service和controller的分工。
    • 2.3 合理使用lombok,减少冗余代码,弃用@Autowired
    • 2.4 实体需要有基类
    • 2.5 dbService的基类
    • 2.6 适度冗余替代表连接,在代码中表连接
    • 2.7 直接封装JDBC处理复杂sql的大屏需求
  • 三、mybatis-plus的一些配置
    • 3.1 framework-mysql模块代码结构
    • 3.2 MybatisPlusConfig
    • 3.3 BaseMetaObjectHandler
    • 3.4 DbDtoUtil
  • 四、业务设计
    • 4.1 涉及实体
  • 五、使用powerdesigner创建数据库表
    • 5.1 展示内容设置
    • 5.2 domain
    • 5.3 数据库表设计
  • 六、使用mybatis-plus生成一些代码
    • 6.1 引入相应库
    • 6.2 编写导出代码
    • 6.3 根据业务组织目录
    • 6.4 nacos 配置
    • 6.4 p6spy 配置

一、目标

1.1 快速开发更有意义

老家通常指资本不发达的2、3线城市,在这样的地方,几乎所有的和软件相关的企业都是国企的附庸。而国企的业务,更多是数据的采集、监控、统计以及流程管理,这与北上广深杭这种一线的互联网环境迥然不同。
国企的业务,通常分2类,一类是在原有系统上迭代开发,这种项目数据和表是既有的,并且可能表设计没有统一的规范,时间、枚举等字段常常放飞自我;一类是做新项目,包装的十分高大上,但其实就是表很多的增删改查,既对效率没有很高的要求,也不牵扯复杂的并发与算法逻辑,稍微费点功夫的可能是节点间的流程控制与数据的导入导出。本篇的设计聚焦第二种。
在这种大背景下,我们更应该关注的是如何快速开发系统;如何让新来的员工更快的融入团队;如何更快的去定位BUG;让领导放心,告(hu)诉(you)他们,说我们有较强的数据安全措施。而对于我们自己而言,更快的做完开发,一来降低项目的风险,二来可以腾出时间摸(xue)鱼(xi),何乐而不为呢。

1.2 拥抱数据库表的变化

在老家的环境中,业务往往面向领导开发。领导可能常常有新鲜的想法,而老家的产品经理往往机不强势,几乎没什么自己的主见。而老家的开发,往往是瀑布式的。产品先用axture画一份没有任何说明文字的文档,而后程序员稀里糊涂的设计好数据库、而后生成代码、之后开发业务。这样一来,在开发过程中,发现数据库表或字段缺失或设计的不合理,那是十分普遍的。
如果使用传统3范式设计数据库,使用mybatis的xml处理表连接join。如果这个时候某些字段发生改变,或者某个实体加点字段,那和他连接的实体的xml中的sql全得改一次,这可真令人头疼。有的时候,领导心血来潮把mysql换postgre,那就更崩溃了。
所以不需要写sql的开发方式就呼之欲出,如果更改了字段,那就直接在实体里改就好了。

1.3 大胆使用枚举

对于类型字段的存储,可以用整型、也可以用字符串、也可以直接使用枚举存储。但数据库里又没有枚举,那怎么办呢。
嗯,在全局的配置中处理一下,前端与服务端交流用字符串,后端与数据库交流使用整型,在代码中直接使用枚举就行了。
为什么我强调使用枚举呢,试想下,如果有的程序员在枚举的存储和判断时,放飞自我,直接在代码里写魔法值。当出现BUG时,大家就会非常的开心。当然也有人说,定义一个枚举的全局变量文件不就行了么。但当别人接手代码时,看到实体中的类型字段使用的是Integer,有可能还没有注释,那也会心中万马奔腾吧~~~

1.4 放心去删除

总会有人担心,我把数据删掉找不回来怎么办?既然这样,我们干脆不去删数据,在数据库表中定义一个象征是否被删除的字段,比如叫is_del。如果该条数据被删了,就置为1,否则为0。
但这样做,有一个大坑大家一定要注意,那就是伪删除和数据库表的唯一索引是不兼容的。如果表中设置了唯一索引,某个数据删掉了,再想加入同样关键字的数据就很麻烦了……。
所以,干脆不设计唯一索引。把发现问题的人解决掉,往往就没有问题了~~~
我们希望ORM框架,可以直接调用delete接口,就帮我们解决这些问题,幸运的是,mybatis-plus是支持的。

1.5 基本的存证记录

国企的数据中,通常希望记录这条数据什么时候创建修改的,什么时候创建修改的,由谁创建修改的,由哪个Ip创建修改的。而我们开发时,不希望在代码中关心这些内容。

1.6 版本-基本的防并发冲突处理

虽说面向企业的开发中,很少遇到并发问题,因为同时在线用户量非常小。但总有人会担心这种问题,害怕两个人同时修改一份数据,导致相互覆盖,产生不好定位的BUG。
这种业务,正常来做需要用redis对数据上锁,当用户打开修改页面后存锁信息,用户完成编辑后释放锁信息。如果有人试图打开特定数据的编辑页面,先查看该数据是否被上锁。如果被上锁则直接抛错。
但实际情况,并不会出现这种两个人修改同一数据的情况,或者极少出现。我们不如简单的做,在实体上加一个版本号,每次修改时验证新的版本号是否比上一次多1,如果不是就报错。所幸,这个功能mybatis-plus中也是有的。

二、制定代码编写规范与思考

2.1 类与字段命名

类别 类名 注释
数据库表 模块前缀_实体名 下划线式
数据库表映射实体po 实体名Entity 驼峰命名
数据定义实体dto 实体名DTO 用于接受前端实体,通常不放id
返回值视图实体vo 实体名VO 返回前端的实体
mapper 实体名Mapper mybatis-plus生成的mapper
daoService接口 I实体名DbService mybatis-plus生成的service接口
daoService实现 实体名DbServiceImpl mybatis-plus生成的service实现
service接口 I实体名Service 真实的service,对应业务模块
service实现 实体名ServiceImpl 真实的service实现,可以注入多个daoService

2.2 mybatis-plus生成service与正常service

我个人观点,mybatis-plus生成的service,其实就是对应于JAP的JpaRepository,故放在dao层,命名加Dao前缀。
对于方法命名,我更倾向于使用JPA的风格,比如findByXXXAndXXX()。对于多条件的分页,直接使用page()。
正常的service对应从业务角度的模块划分,原则上service不允许相互注入,但service可以注入多个daoService

2.3 service和controller的分工。

通常,在controller中做前端数据的校验,并调用service层,执行特定的逻辑。

2.3 合理使用lombok,减少冗余代码,弃用@Autowired

使用lombok的@Data注解,不必在实体中写get,set方法
在controller或service上加上@RequiredArgsConstructor,把需要注入的组件定义成final的
这样lombok就会自动生成带有所有final字段的构造函数。
十分不推荐使用@Autowired,因为@Autowired不保证一定注入成功,可能造成莫名其妙的bug。
使用构造函数注入,还能间接指定各组建初始化的顺序。
使用@Slf4j注解,便于打log

2.4 实体需要有基类

对于系统生成的实体,不需要记录是谁创建修改的,其基类代码如下。

@Data
public class SysBaseEntity {
    @Schema(title = "主键")
    @TableId(type = IdType.ASSIGN_ID)
    protected Long id;
    @JSONField(serialize = false, deserialize = false)
    @TableField(fill = FieldFill.INSERT)
    @Schema(title = "创建时间")
    protected LocalDateTime createTime;
    @JSONField(serialize = false, deserialize = false)
    @TableField(fill = FieldFill.UPDATE)
    @Schema(title = "修改时间")
    protected LocalDateTime modifyTime;
    @Schema(title = "版本")
    @Version
    protected Integer version;

    public void createInit() {
        id = IdWorker.getId();
        // 靠全局配置填充
        createTime = null;
        modifyTime = null;
        version = 0;
    }

    public void updateInit() {
        modifyTime = null;
    }
}

对于用户创建的实体,需要记录操作者的信息,其代码如下:

@Data
public class BaseEntity extends SysBaseEntity{

    @JSONField(serialize = false, deserialize = false)
    @TableField(fill = FieldFill.INSERT)
    @Schema(title = "创建用户Id")
    protected Long createBy;
    @JSONField(serialize = false, deserialize = false)
    @TableField(fill = FieldFill.INSERT)
    @Schema(title = "创建用户昵称")
    protected String createName;
    @JSONField(serialize = false, deserialize = false)
    @TableField(fill = FieldFill.INSERT)
    @Schema(title = "创建者Ip")
    protected String createIp;
    @JSONField(serialize = false, deserialize = false)
    @TableField(fill = FieldFill.UPDATE)
    @Schema(title = "修改用户Id")
    protected Long modifyBy;
    @JSONField(serialize = false, deserialize = false)
    @TableField(fill = FieldFill.UPDATE)
    @Schema(title = "修改用户昵称")
    protected String modifyName;
    @JSONField(serialize = false, deserialize = false)
    @TableField(fill = FieldFill.UPDATE)
    @Schema(title = "创建者Ip")
    protected String modifyIp;
    @JSONField(serialize = false, deserialize = false)
    @TableLogic
    @Schema(title = "是否删除")
    protected boolean del;

    @Override
    public void createInit() {
        super.createInit();
        // 靠全局配置填充
        createBy = null;
        createName = null;
        createIp = null;

        modifyBy = null;
        modifyName = null;
        modifyIp = null;

        del = false;
    }

    @Override
    public void updateInit() {
        super.updateInit();
        modifyBy = null;
        modifyName = null;
        modifyIp = null;
    }
}

2.5 dbService的基类

在实际开发中,我们通常不会直接使用mybatis-plus提供的通用DbService。我们会自己写一个类继承mybatis-plus的类,在里面增加一些共通的接口和实现。本章添加一些方便返回Vo的分页和列表接口。

IZfDbService:

public interface IZfDbService<T> extends IService<T> {
    <VO>Page<VO> page(IPage page, Wrapper<T> queryWrapper, Class<VO> cls);

    <VO>Page<VO> page(IPage page, Wrapper<T> queryWrapper, Class<VO> cls, Function<T,VO> convertor);

    <VO> List<VO> list(Wrapper<T> queryWrapper, Class<VO> cls);

    <VO> List<VO> list(Wrapper<T> queryWrapper, Class<VO> cls, Function<T,VO> convertor);

    T findOne(Wrapper<T> queryWrapper);

    boolean existBy(Wrapper<T> queryWrapper);
}

ZfDbServiceImpl:

public class ZfDbServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements IZfDbService<T> {

    @Override
    public <VO> Page<VO> page(IPage page, Wrapper<T> queryWrapper, Class<VO> cls) {
        IPage<T> pageData = super.page(page,queryWrapper);
        Page<VO> pageVoData = new Page<VO>(pageData.getCurrent(),pageData.getSize());
        pageVoData.setTotal(pageData.getTotal());
        pageVoData.setPages(pageData.getPages());
        List<VO> voList = DtoEntityUtil.trans(pageData.getRecords(),cls);
        pageVoData.setRecords(voList);
        return pageVoData;
    }

    @Override
    public <VO> Page<VO> page(IPage page, Wrapper<T> queryWrapper, Class<VO> cls, Function<T, VO> convertor) {
        IPage<T> pageData = super.page(page,queryWrapper);
        Page<VO> pageVoData = new Page<VO>(pageData.getCurrent(),pageData.getSize());
        pageVoData.setTotal(pageData.getTotal());
        pageVoData.setPages(pageData.getPages());
        List<VO> voList = new ArrayList<>(pageData.getRecords().size());
        for(T data : pageData.getRecords()){
            VO voData = convertor.apply(data);
            voList.add(voData);
        }
        pageVoData.setRecords(voList);
        return pageVoData;
    }

    @Override
    public <VO> List<VO> list(Wrapper<T> queryWrapper, Class<VO> cls) {
        List<T> listData = super.list(queryWrapper);
        List<VO> listVoData = DtoEntityUtil.trans(listData,cls);
        return listVoData;
    }

    @Override
    public <VO> List<VO> list(Wrapper<T> queryWrapper, Class<VO> cls, Function<T, VO> convertor) {
        List<T> listData = super.list(queryWrapper);
        List<VO> listVoData = new ArrayList<>();
        for(T data : listData){
            VO voData = convertor.apply(data);
            listVoData.add(voData);
        }
        return listVoData;
    }

    @Override
    public T findOne(Wrapper<T> queryWrapper) {
        List<T> listData = super.list(queryWrapper);
        if(CollectionUtils.isEmpty(listData)){
            return null;
        }
        return listData.get(0);
    }

    @Override
    public boolean existBy(Wrapper<T> queryWrapper) {
        long cnt = baseMapper.selectCount(queryWrapper);
        return cnt > 0;
    }
}

2.6 适度冗余替代表连接,在代码中表连接

在实际项目中,有些数据创建好后几乎不可能改变,比如用户名。但对于这种不会改变的信息,没必要每次再查一遍,这样不但执行效率低,些起来还很麻烦。

2.7 直接封装JDBC处理复杂sql的大屏需求

对于一些大屏需求,常常会设计极其复杂的表连接和聚合查询。这个时候定义实体已经完全没有意义。
这时候我们不妨封装JDBC,建一个查询的表,存储查询的大sql。可以结合freemark,使用xml使sql动态化。服务只需调用特定的键,就会执行对应的sql。sql里写的什么样的值,就直接返回前端就好。

三、mybatis-plus的一些配置

3.1 framework-mysql模块代码结构

芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第1张图片

3.2 MybatisPlusConfig

这个配置类负责mybatis-plus的分页和乐观锁

@AllArgsConstructor
@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();

        // 分页
        PaginationInnerInterceptor mysqlInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        interceptor.addInnerInterceptor(mysqlInnerInterceptor);

        // 乐观锁
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

3.3 BaseMetaObjectHandler

这个基类负责mybatis-plus的自动填充功能,在具体的业务服务器写一个类继承他便可使用

@Slf4j
public abstract class BaseMetaObjectHandler implements MetaObjectHandler {

    protected static final Long SYS_ID = 0L;
    protected static final String SYS_NAME = "sys";
    protected static final String SYS_IP = "localhost";

    protected String createTimeStr = "createTime";
    protected String createByStr= "createBy";
    protected String createByNameStr = "createByName";
    protected String createIpStr = "createIp";

    protected String modifyTimeStr = "modifyTime";
    protected String modifyByStr= "modifyBy";
    protected String modifyByNameStr = "modifyByName";
    protected String modifyIpStr = "modifyIp";


    protected void insertCreateTime(MetaObject pMetaObject){
        if(pMetaObject.hasSetter(createTimeStr)){
            Object orgCreateTime = pMetaObject.getValue(createTimeStr);
            if(null == orgCreateTime){
                this.strictInsertFill(pMetaObject,createTimeStr,()-> LocalDateTime.now(),LocalDateTime.class);
            }
        }
    }

    protected abstract void insertCreateBy(MetaObject pMetaObject);

    protected abstract void insertCreateByName(MetaObject pMetaObject);

    protected abstract void insertCreateByIp(MetaObject pMetaObject);

    abstract protected void otherInsertFill(MetaObject pMetaObject);

    abstract protected boolean hasTokenObject();

    @Override
    public void insertFill(MetaObject pMetaObject) {
        insertCreateTime(pMetaObject);
        if(hasTokenObject()){
            try{
                insertCreateBy(pMetaObject);
                insertCreateByName(pMetaObject);
                insertCreateByIp(pMetaObject);
            }catch (Exception ex){
                log.error("insertFill 出现异常",ex);
            }
        }else{
            this.strictInsertFill(pMetaObject,createByStr,()->SYS_ID,Long.class);
            this.strictInsertFill(pMetaObject,createByNameStr,()->SYS_NAME,String.class);
            this.strictInsertFill(pMetaObject,createIpStr,()->SYS_IP,String.class);
        }
        otherInsertFill(pMetaObject);
    }

    protected void updateModifyTime(MetaObject pMetaObject){
        if(pMetaObject.hasSetter(modifyTimeStr)){
            Object orgModifyTime = pMetaObject.getValue(modifyTimeStr);
            if(null == orgModifyTime){
                this.strictUpdateFill(pMetaObject,modifyTimeStr,()-> LocalDateTime.now(),LocalDateTime.class);
            }
        }
    }

    protected abstract void updateModifyBy(MetaObject pMetaObject);

    protected abstract void updateModifyByName(MetaObject pMetaObject);

    protected abstract void updateModifyByIp(MetaObject pMetaObject);

    abstract protected void otherUpdateFill(MetaObject pMetaObject);


    @Override
    public void updateFill(MetaObject pMetaObject) {
        updateModifyTime(pMetaObject);
        if(hasTokenObject()){
            try{
                updateModifyBy(pMetaObject);
                updateModifyByName(pMetaObject);
                updateModifyTime(pMetaObject);
                updateModifyByIp(pMetaObject);
            }catch (Exception ex){
                log.error("updateFill 出现异常",ex);
            }
        }else{
            this.strictUpdateFill(pMetaObject,modifyByStr,()->SYS_ID,Long.class);
            this.strictUpdateFill(pMetaObject,modifyByNameStr,()->SYS_NAME,String.class);
            this.strictUpdateFill(pMetaObject,modifyIpStr,()->SYS_IP,String.class);
        }
        otherUpdateFill(pMetaObject);
    }
}

由于本期没有接鉴权,直接把hasTokenObject返回false,其他函数空白即可

@Component
public class MetaObjectHandler extends BaseMetaObjectHandler {
@Override
    protected boolean hasTokenObject() {
        return false;
    }
}

3.4 DbDtoUtil

在第一讲中的DtoUtil下,添加如下代码

public class DbDtoEntityUtil extends DtoEntityUtil {

    /**
     * 用于新增
     *
     * @param pDto   dto实体
     * @param clazz  po的class
     * @return 初始化好的po
     */
    public static <D, E> E dtoToPo(D pDto, Class<E> clazz) {
        if (pDto == null) {
            return null;
        }
        E result = mapper.map(pDto, clazz);
        if(result instanceof BaseEntity){
            ((BaseEntity) result).createInit();
        }
        return result;
    }

    /**
     * 用于更新
     *
     * @param  pDto      dto实体
     * @param  pPo       待更新的po实体
     * @param  pPoClass  po的类
     * @return 更新后的po
     */
    public static <D, E> E dtoToPo(D pDto, E pPo, Class<E> pPoClass) {
        if (pDto == null) {
            return null;
        }
        E result = mapper.map(pPo, pPoClass);
        copy(result,pDto);
        if(result instanceof BaseEntity){
            ((BaseEntity) result).updateInit();
        }
        return result;
    }
}

四、业务设计

我们可以模拟一个PCR公会战报刀系统。
实体部分:允许创建角色,创建公会,加入公会,公会内统计,公会排名。
游戏部分:允许举办公会战,报刀,尾刀,查看会战状态等。
本节着重讨论数据库相关操作,故业务上只讨论表的创建

4.1 涉及实体

user Long id String name Long union_id union Long id String name Integer rank Long scoreId player_score Long id Long player_id String player_name Long battle_id Integer score Integer demage player_battle_day Long id Long player_id String player_name Long battle_id Integer day Integer hit_whole Integer hit_tail Integer hit_compensate Boolean sl battle Long id Long union_id Integer year EConstellation constellation Long boss_id Integer round Integer boss_idx int day EBattleStatus status union_score Long id Long union_id String union_name Long battle_id Integer year EConstellation constellation int round int boss_idx int score boss Long id Long battle_id EBossType boss_type Integer hp bool alive Integer phase Integer round hit Long id Long user_id String user_name Long union_id Long battle_id Long boss_id bool done int demage int score EHitType hit_type Integer day Integer hit_index sysData Stirng key EValueType type String value union_id-id player_id-id player_id-id id-union_id id-union_id id-union_id id-user_id id-boss_id

五、使用powerdesigner创建数据库表

5.1 展示内容设置

在空白处单击右键,选择display preference>>table>>advanced
记得把这个页面的Indexes 和 Comment勾上
芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第2张图片
而后点击colomn,设置为如图
芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第3张图片

5.2 domain

domain我通常不做复杂的设计,以好用方便为主
芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第4张图片

5.3 数据库表设计

根据4.1,数据库设计如图所示:
芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第5张图片
芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第6张图片

而后,点击菜单的database,选择generate database,生成数据库文件。
再然后把数据库文件在数据库中执行,就得到我们业务所需的表。

六、使用mybatis-plus生成一些代码

6.1 引入相应库

上一集我们已经建好了mysql模块的pom库,我们需要在上期的pom中添加生成代码相关的库
为方便起见,展示所有的pom代码:

<dependencies>
        <dependency>
            <groupId>indi.zhifa.recipegroupId>
            <artifactId>framework-commonartifactId>
        dependency>
        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-boot-starterartifactId>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-extensionartifactId>
        dependency>
        <dependency>
            <groupId>p6spygroupId>
            <artifactId>p6spyartifactId>
        dependency>
        
        <dependency>
            <groupId>com.baomidougroupId>
            <artifactId>mybatis-plus-generatorartifactId>
        dependency>
        <dependency>
            <groupId>org.freemarkergroupId>
            <artifactId>freemarkerartifactId>
        dependency>
        
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <scope>providedscope>
        dependency>
    dependencies>

6.2 编写导出代码

outputPath中我设置的是我电脑的路径,大家可以自行替换

public class MapperGenerator {
    public static void main(String[] args) throws FileNotFoundException {
        String outputPath =  "E:\\DOCUMENT\\generator\\bailan2";

        FastAutoGenerator.create("jdbc:mysql://localhost:3307/bailan?useSSL=false&useUnicode=true&characterEncoding=utf-8",
                        "root", "qqhilvMgAl@7")
                .globalConfig(builder -> {
                    // 设置作者
                    builder.author("织法")
                            // 开启 swagger 模式
                            .enableSwagger()
                            // 覆盖已生成文件
                            .fileOverride()
                            // 指定输出目录
                            .outputDir(outputPath);
                })
                .packageConfig(builder -> {
                    // 设置父包名
                    builder.parent("indi.zhifa.recipe.bailan.busy")
                            // 指定模块名称
                            .moduleName("dbgen")
                            .entity("entity.po")
                            .service("dao.service")
                            .serviceImpl("dao.service.impl")
                            .mapper("dao.mapper");

                })
                .strategyConfig(builder -> {
                    // 设置过滤表前缀
                    builder.addTablePrefix("bl_")
                            .addExclude("gc_user")
                            .entityBuilder()
                            .enableLombok()
                            .enableRemoveIsPrefix()
                            .logicDeleteColumnName("del")
                            .logicDeletePropertyName("del")
                            .addTableFills(new Column("create_time", FieldFill.INSERT))
                            .addTableFills(new Property("createTime", FieldFill.INSERT))
                            .addTableFills(new Column("create_by",FieldFill.INSERT))
                            .addTableFills(new Property("createBy",FieldFill.INSERT))
                            .addTableFills(new Column("create_name",FieldFill.INSERT))
                            .addTableFills(new Property("createName",FieldFill.INSERT))
                            .addTableFills(new Column("create_ip",FieldFill.INSERT))
                            .addTableFills(new Property("createIp",FieldFill.INSERT))

                            .addTableFills(new Column("modify_time", FieldFill.UPDATE))
                            .addTableFills(new Property("modifyTime", FieldFill.UPDATE))
                            .addTableFills(new Column("modify_by",FieldFill.UPDATE))
                            .addTableFills(new Property("modifyBy",FieldFill.UPDATE))
                            .addTableFills(new Column("modify_name",FieldFill.UPDATE))
                            .addTableFills(new Property("modifyName",FieldFill.UPDATE))
                            .addTableFills(new Column("modify_ip",FieldFill.UPDATE))
                            .addTableFills(new Property("modifyIp",FieldFill.UPDATE))

                            .idType(IdType.ASSIGN_ID)
                            .formatFileName("%sEntity")
                            .entityBuilder()
                            .superClass(BaseEntity.class)
                            .disableSerialVersionUID()
                            .enableLombok()
                            .versionColumnName("version")
                            .versionPropertyName("version")
                            .addSuperEntityColumns("id","create_time","create_by","create_name","create_ip","modify_by","modify_time","modify_name","modify_ip","version","del")
                            .serviceBuilder()
                            .formatServiceFileName("I%sDbService")
                            .formatServiceImplFileName("%sDbServiceImpl")
                            .serviceBuilder()
                            .superServiceClass(IZfDbService.class)
                            .superServiceImplClass(ZfDbServiceImpl.class);
                })
                // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .templateEngine(new FreemarkerTemplateEngine())
                .execute();
    }
}

执行程序,打开dbgen文件夹,把我们不需要的代码删掉,如controller以及生成的xml文件。
然后把该文件夹拖进idea,以便之后组织代码
芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第7张图片

6.3 根据业务组织目录

通常,业务量比较小的时候,把po放一个目录,mapper放一个目录,dbService放一个目录,service放一个目录,这样更体现一种分层开发的思想。理论上上层组件只能调用下层的组件,不可以平级调用或者调用更上层的。在同一层也方便编写拦截器做一些统一处理。
但真是的开发中,业务量往往很大,同一模块还有很多辅助表,如果用这种方式组织代码,就会导致代码的查找极其麻烦,写着写着就写晕的情况时有发生。
故我更推荐把相同模块放入同一个文件夹中。
首先在一个包下建好目录,而后再把这个目录复制到各种包下面:
芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第8张图片
手动拖改后变成这个样子:
芝法酱躺平攻略(2)——SpringBoot下使用mybatis-plus的mysql操作_第9张图片

6.4 nacos 配置

spring:
  application:
    name: bailan2
  pathmatch:
    matching-strategy: ANT_PATH_MATCHER
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        register-enabled: true
  datasource:
    #数据库配置
    #driver-class-name: com.mysql.cj.jdbc.Driver
    #平时开发用这个,便于看sql的log
    driver-class-name: com.p6spy.engine.spy.P6SpyDriver
    #url: jdbc:mysql://localhost:3307/bailan?useSSL=false&useUnicode=true&characterEncoding=utf-8
    url: jdbc:p6spy:mysql://localhost:3307/bailan?useSSL=false&useUnicode=true&characterEncoding=utf-8
    username: 芝法酱
    password: 我才不告诉你
    hikari:
      # 连接池最大连接数,默认是10
      maximum-pool-size: 100
      # 最小空闲链接
      minimum-idle: 5
      # 空闲连接存活最大时间,默认 600000(10分钟)
      idle-timeout: 600000
      # 数据库连接超时时间,默认30秒,即30000
      connection-timeout: 30000
      # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟;正在使用的连接永远不会退休,只有在关闭后才会被删除。
      max-lifetime: 1800000
      # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
      auto-commit: true
      pool-name: Hikari


swagger:
  enable: true
  group-name: "业务接口"
  api-package: indi.zhifa.recipe.bailan.busy.controller.api
  api-regex: "/api/**"
  title: "母猪焊接会战管理接口"
  description: "母猪焊接会战管理接口"
  version: "1.0.0"
  name: "芝法酱"
  email: "[email protected]"
  url: "https://github.com/hataksumo"

mybatis-plus:
  typeEnumsPackage: indi.zhifa.recipe.bailan.busy.enums

app:
  battle-days: 6
  day-hits: 3
  page-size: 100
  max-union-player-num: 30

logging:
  level:
    indi.zhifa.recipe.bailan: info

6.4 p6spy 配置

在业务服务的resource下建1个spy.properties

modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
deregisterdrivers=true
useprefix=true
excludecategories=info,debug,result,commit,resultset
dateformat=yyyy-MM-dd HH:mm:ss
outagedetection=true
outagedetectioninterval=2

你可能感兴趣的:(springboot,mybatis-plus,spring,boot,mysql)