An open source framework that increases your work efficiency by at least twice, and you can spend more time fishing

All programmers who have already worked in the industry know that most of our working time is actually writing business code, and most of the business code is if, else and CRUD operations, so the key to improving work efficiency is to keep the CRUD time short enough. So here is an example of such an open source job to improve your work efficiency.

 Hello everyone, let me introduce myself first. I'm on the code. You can call me Brother Code. I am also the most ordinary student who graduated from an ordinary undergraduate degree. I believe that most programmers or those who want to work in the programmer industry are I am a child of an ordinary family, so I also rely on my own efforts, from graduation to join a traditional company, to changing jobs without fail, and now working in a giant company in the Internet industry, I hope that through my sharing, I can help everyone

I have prepared 16 technical columns for everyone to lead you to learn together

"Billion-level traffic distributed system combat"

"Battery Factory Interview Must-Ask Series"

"Technical Talk"

"Zero Foundation takes you to learn java tutorial column"

"Take you to learn springCloud column"

"Take you to learn SpringCloud source code column"

"Take you to learn distributed system column"

"Take you to learn cloud native column"

"Take you to learn springboot source code"

"Take you to learn netty principles and practical column"

"Take you to learn Elasticsearch column"

"Take you to learn mysql column"

"Take you to learn JVM principle column"

"Take you to learn Redis principle column"

"Take you to learn java advanced column"

"Take you to learn big data column"

I believe that the ORM framework used by many friends in the project is MyBatis. If you only use MyBatis to operate the database, you need to write a lot of SQL implementations for single-table queries. At this time, we often choose an enhanced tool to implement these single-table CRUD operations. Here we recommend a useful tool, MyBatis-Plus!

About MyBatis-Plus

MyBatis-Plus (MP for short) is an enhancement tool for MyBatis. It only enhances and does not change on the basis of MyBatis. It is born to simplify development and improve efficiency. MyBatis-Plus provides a code generator, which can generate controller, service, mapper, model, mapper.xml code with one click, and provides a wealth of CRUD operation methods to help us free our hands!

MyBatis-Plus integration

First, we need to integrate MyBatis-Plus in the SpringBoot project, and then we will introduce its usage in detail!

  • Add related dependencies in pom.xml, mainly MyBatis-Plus, MyBatis-Plus Generator and Velocity template engine;
<dependencies>
    <!--Mybatis-Plus依赖-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.2</version>
    </dependency>
    <!--Mybatis-Plus代码生成器-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-generator</artifactId>
        <version>3.3.2</version>
    </dependency>
    <!--Velocity模板生成引擎-->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity-engine-core</artifactId>
        <version>2.2</version>
    </dependency>
</dependencies>
  • Add the following configuration to the SpringBoot configuration file application.yml, configure the data source and MyBatis-Plus;
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml #指定mapper.xml路径
  global-config:
    db-config:
      id-type: auto #全局默认主键类型设置为自增
  configuration:
    auto-mapping-behavior: partial #只对非嵌套的 resultMap 进行自动映射
    map-underscore-to-camel-case: true #开启自动驼峰命名规则映射Copy to clipboardErrorCopied
  • Add the Java configuration of MyBatis-Plus, and use @MapperScanannotations to configure the Mapper interface path that needs to be scanned. MyBatis-Plus has its own paging function, and you need to configure the paging plug-in PaginationInterceptor.
/**
 * MyBatis配置类
 * Created by macro on 2019/4/8.
 */
@Configuration
@MapperScan("com.macro.mall.tiny.modules.*.mapper")
public class MyBatisConfig {
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
        return paginationInterceptor;
    }
}

Code generator

MyBatis-Plus provides a code generator, which can generate controller, service, mapper, model, mapper.xml code with one click, which is very convenient!

  • First, we create a code generator class MyBatisPlusGenerator, and directly run its mainmethods to generate relevant code;
/**
 * MyBatisPlus代码生成器
 * Created by macro on 2020/8/20.
 */
public class MyBatisPlusGenerator {

    public static void main(String[] args) {
        String projectPath = System.getProperty("user.dir") + "/mall-tiny-plus";
        String moduleName = scanner("模块名");
        String[] tableNames = scanner("表名,多个英文逗号分割").split(",");
        // 代码生成器
        AutoGenerator autoGenerator = new AutoGenerator();
        autoGenerator.setGlobalConfig(initGlobalConfig(projectPath));
        autoGenerator.setDataSource(initDataSourceConfig());
        autoGenerator.setPackageInfo(initPackageConfig(moduleName));
        autoGenerator.setCfg(initInjectionConfig(projectPath, moduleName));
        autoGenerator.setTemplate(initTemplateConfig());
        autoGenerator.setStrategy(initStrategyConfig(tableNames));
        autoGenerator.setTemplateEngine(new VelocityTemplateEngine());
        autoGenerator.execute();
    }

    /**
     * 读取控制台内容信息
     */
    private static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        System.out.println(("请输入" + tip + ":"));
        if (scanner.hasNext()) {
            String next = scanner.next();
            if (StrUtil.isNotEmpty(next)) {
                return next;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "!");
    }

    /**
     * 初始化全局配置
     */
    private static GlobalConfig initGlobalConfig(String projectPath) {
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(projectPath + "/src/main/java");
        globalConfig.setAuthor("macro");
        globalConfig.setOpen(false);
        globalConfig.setSwagger2(true);
        globalConfig.setBaseResultMap(true);
        globalConfig.setFileOverride(true);
        globalConfig.setDateType(DateType.ONLY_DATE);
        globalConfig.setEntityName("%s");
        globalConfig.setMapperName("%sMapper");
        globalConfig.setXmlName("%sMapper");
        globalConfig.setServiceName("%sService");
        globalConfig.setServiceImplName("%sServiceImpl");
        globalConfig.setControllerName("%sController");
        return globalConfig;
    }

    /**
     * 初始化数据源配置
     */
    private static DataSourceConfig initDataSourceConfig() {
        Props props = new Props("generator.properties");
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl(props.getStr("dataSource.url"));
        dataSourceConfig.setDriverName(props.getStr("dataSource.driverName"));
        dataSourceConfig.setUsername(props.getStr("dataSource.username"));
        dataSourceConfig.setPassword(props.getStr("dataSource.password"));
        return dataSourceConfig;
    }

    /**
     * 初始化包配置
     */
    private static PackageConfig initPackageConfig(String moduleName) {
        Props props = new Props("generator.properties");
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setModuleName(moduleName);
        packageConfig.setParent(props.getStr("package.base"));
        packageConfig.setEntity("model");
        return packageConfig;
    }

    /**
     * 初始化模板配置
     */
    private static TemplateConfig initTemplateConfig() {
        TemplateConfig templateConfig = new TemplateConfig();
        //可以对controller、service、entity模板进行配置
        //mapper.xml模板需单独配置
        templateConfig.setXml(null);
        return templateConfig;
    }

    /**
     * 初始化策略配置
     */
    private static StrategyConfig initStrategyConfig(String[] tableNames) {
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setNaming(NamingStrategy.underline_to_camel);
        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
        strategyConfig.setEntityLombokModel(true);
        strategyConfig.setRestControllerStyle(true);
        //当表名中带*号时可以启用通配符模式
        if (tableNames.length == 1 && tableNames[0].contains("*")) {
            String[] likeStr = tableNames[0].split("_");
            String likePrefix = likeStr[0] + "_";
            strategyConfig.setLikeTable(new LikeTable(likePrefix));
        } else {
            strategyConfig.setInclude(tableNames);
        }
        return strategyConfig;
    }

    /**
     * 初始化自定义配置
     */
    private static InjectionConfig initInjectionConfig(String projectPath, String moduleName) {
        // 自定义配置
        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                // 可用于自定义属性
            }
        };
        // 模板引擎是Velocity
        String templatePath = "/templates/mapper.xml.vm";
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + moduleName
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        injectionConfig.setFileOutConfigList(focList);
        return injectionConfig;
    }

}
  • Then resourcesadd a configuration file in the directory, add generator.propertiesthe data source configuration of the code generator and the basic package name for storing the business code;
dataSource.url=jdbc:mysql://localhost:3306/mall?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
dataSource.driverName=com.mysql.cj.jdbc.Driver
dataSource.username=root
dataSource.password=root
package.base=com.macro.mall.tiny.modules
  • Careful friends can find MyBatisPlusGeneratorthat many configuration codes are not commented. In fact, the Chinese comments in the source code of MyBatis-Plus are very complete, just check the source code, here is an excerpt DataSourceConfigof the source code in a paragraph;
/**
 * 数据库配置
 *
 * @author YangHu, hcl
 * @since 2016/8/30
 */
@Data
@Accessors(chain = true)
public class DataSourceConfig {

    /**
     * 数据库信息查询
     */
    private IDbQuery dbQuery;
    /**
     * 数据库类型
     */
    private DbType dbType;
    /**
     * PostgreSQL schemaName
     */
    private String schemaName;
    /**
     * 类型转换
     */
    private ITypeConvert typeConvert;
    /**
     * 关键字处理器
     * @since 3.3.2
     */
    private IKeyWordsHandler keyWordsHandler;
    /**
     * 驱动连接的URL
     */
    private String url;
    /**
     * 驱动名称
     */
    private String driverName;
    /**
     * 数据库连接用户名
     */
    private String username;
    /**
     * 数据库连接密码
     */
    private String password;

    //省略若干代码......

}
  • The code generator supports two modes, one is to generate the code of a single table, for example, only the pms_brandtable code can be input first pms, and then input pms_brand;

  • Generate a list of single-table code structure;

  • Another type of code that directly generates the entire module requires wildcards *. For example, to generate the umsmodule code, you can enter it first umsand then enter it ums_*;

  • Generate an overview of the entire module code structure.

custom build template

MyBatis-Plus uses a template engine to generate code, and supports Velocity (default), Freemarker, and Beetl template engines. Here, Velocity is used to introduce how to customize the generated template.

  • First, we can find the default template from the source code of the MyBatis-Plus Generator dependency package and copy it to the project resources/templatesdirectory;

  • Configure it in the MyBatisPlusGeneratorclass TemplateConfigand configure the path of each template;
/**
 * MyBatisPlus代码生成器
 * Created by macro on 2020/8/20.
 */
public class MyBatisPlusGenerator {

    /**
     * 初始化模板配置
     */
    private static TemplateConfig initTemplateConfig() {
        TemplateConfig templateConfig = new TemplateConfig();
        //可以对controller、service、entity模板进行配置
        templateConfig.setEntity("templates/entity.java");
        templateConfig.setMapper("templates/mapper.java");
        templateConfig.setController("templates/controller.java");
        templateConfig.setService("templates/service.java");
        templateConfig.setServiceImpl("templates/serviceImpl.java");
        //mapper.xml模板需单独配置
        templateConfig.setXml(null);
        return templateConfig;
    }
}Copy to clipboardErrorCopied
  • Customize the template. During the customization process, we can find many built-in variables, which are used to output to the template. Here, the service.java.vmtemplate is used as an example, for example package, tablethese variables;
package ${package.Service};

import ${package.Entity}.${entity};
import ${superServiceClassPackage};

/**
 * <p>
 * $!{table.comment} 服务类
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
#if(${kotlin})
interface ${table.serviceName} : ${superServiceClass}<${entity}>
#else
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {

}
#endCopy to clipboardErrorCopied
  • Understanding where these variables come from is very helpful for us to customize the template. In fact, these variables all come from different AbstractTemplateEnginemethods . For getObjectMapspecific variables, please refer to the source code.
/**
 * 模板引擎抽象类
 *
 * @author hubin
 * @since 2018-01-10
 */
public abstract class AbstractTemplateEngine {
        /**
         * 渲染对象 MAP 信息
         *
         * @param tableInfo 表信息对象
         * @return ignore
         */
        public Map<String, Object> getObjectMap(TableInfo tableInfo) {
            Map<String, Object> objectMap = new HashMap<>(30);
            ConfigBuilder config = getConfigBuilder();
            if (config.getStrategyConfig().isControllerMappingHyphenStyle()) {
                objectMap.put("controllerMappingHyphenStyle", config.getStrategyConfig().isControllerMappingHyphenStyle());
                objectMap.put("controllerMappingHyphen", StringUtils.camelToHyphen(tableInfo.getEntityPath()));
            }
            objectMap.put("restControllerStyle", config.getStrategyConfig().isRestControllerStyle());
            objectMap.put("config", config);
            objectMap.put("package", config.getPackageInfo());
            GlobalConfig globalConfig = config.getGlobalConfig();
            objectMap.put("author", globalConfig.getAuthor());
            objectMap.put("idType", globalConfig.getIdType() == null ? null : globalConfig.getIdType().toString());
            objectMap.put("logicDeleteFieldName", config.getStrategyConfig().getLogicDeleteFieldName());
            objectMap.put("versionFieldName", config.getStrategyConfig().getVersionFieldName());
            objectMap.put("activeRecord", globalConfig.isActiveRecord());
            objectMap.put("kotlin", globalConfig.isKotlin());
            objectMap.put("swagger2", globalConfig.isSwagger2());
            objectMap.put("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
            objectMap.put("table", tableInfo);
            objectMap.put("enableCache", globalConfig.isEnableCache());
            objectMap.put("baseResultMap", globalConfig.isBaseResultMap());
            objectMap.put("baseColumnList", globalConfig.isBaseColumnList());
            objectMap.put("entity", tableInfo.getEntityName());
            objectMap.put("entitySerialVersionUID", config.getStrategyConfig().isEntitySerialVersionUID());
            objectMap.put("entityColumnConstant", config.getStrategyConfig().isEntityColumnConstant());
            objectMap.put("entityBuilderModel", config.getStrategyConfig().isEntityBuilderModel());
            objectMap.put("chainModel", config.getStrategyConfig().isChainModel());
            objectMap.put("entityLombokModel", config.getStrategyConfig().isEntityLombokModel());
            objectMap.put("entityBooleanColumnRemoveIsPrefix", config.getStrategyConfig().isEntityBooleanColumnRemoveIsPrefix());
            objectMap.put("superEntityClass", getSuperClassName(config.getSuperEntityClass()));
            objectMap.put("superMapperClassPackage", config.getSuperMapperClass());
            objectMap.put("superMapperClass", getSuperClassName(config.getSuperMapperClass()));
            objectMap.put("superServiceClassPackage", config.getSuperServiceClass());
            objectMap.put("superServiceClass", getSuperClassName(config.getSuperServiceClass()));
            objectMap.put("superServiceImplClassPackage", config.getSuperServiceImplClass());
            objectMap.put("superServiceImplClass", getSuperClassName(config.getSuperServiceImplClass()));
            objectMap.put("superControllerClassPackage", verifyClassPacket(config.getSuperControllerClass()));
            objectMap.put("superControllerClass", getSuperClassName(config.getSuperControllerClass()));
            return Objects.isNull(config.getInjectionConfig()) ? objectMap : config.getInjectionConfig().prepareObjectMap(objectMap);
        }
}

CRUD operations

The power of MyBatis-Plus is not only its code generation function, but also that it provides a wealth of CRUD methods, allowing us to implement single-table CRUD almost without writing SQL!

  • The interface we generated before has various CRUD methods directly PmsBrandMapperbecause it inherits the BaseMapperinterface;
/**
 * <p>
 * 品牌表 Mapper 接口
 * </p>
 *
 * @author macro
 * @since 2020-08-20
 */
public interface PmsBrandMapper extends BaseMapper<PmsBrand> {

}
  • Let's see if BaseMapperthe method in the following can basically meet our daily needs;

  • The interface we generated earlier also has various CRUD methods PmsBrandServicebecause it inherits the IServiceinterface;
/**
 * <p>
 * 品牌表 服务类
 * </p>
 *
 * @author macro
 * @since 2020-08-20
 */
public interface PmsBrandService extends IService<PmsBrand> {

}
  • It can be seen that the ratio BaseMapperis more abundant;

  • With the methods provided in these IServiceand BaseMapperin, we almost do not need to write SQL to implement single-table queries, and it PmsBrandControlleris easier to use MyBatis-Plus to implement the previous methods!
/**
 * <p>
 * 品牌表 前端控制器
 * </p>
 *
 * @author macro
 * @since 2020-08-20
 */
@Api(tags = "PmsBrandController", description = "商品品牌管理")
@RestController
@RequestMapping("/brand")
public class PmsBrandController {

    private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);

    @Autowired
    private PmsBrandService brandService;

    @ApiOperation("获取所有品牌列表")
    @RequestMapping(value = "/listAll", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<PmsBrand>> getBrandList() {
        return CommonResult.success(brandService.list());
    }

    @ApiOperation("添加品牌")
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
        CommonResult commonResult;
        boolean result = brandService.save(pmsBrand);
        if (result) {
            commonResult = CommonResult.success(pmsBrand);
            LOGGER.debug("createBrand success:{}", pmsBrand);
        } else {
            commonResult = CommonResult.failed("操作失败");
            LOGGER.debug("createBrand failed:{}", pmsBrand);
        }
        return commonResult;
    }

    @ApiOperation("更新指定id品牌信息")
    @RequestMapping(value = "/update", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult updateBrand(@RequestBody PmsBrand pmsBrand) {
        CommonResult commonResult;
        boolean result = brandService.updateById(pmsBrand);
        if (result) {
            commonResult = CommonResult.success(pmsBrand);
            LOGGER.debug("updateBrand success:{}", pmsBrand);
        } else {
            commonResult = CommonResult.failed("操作失败");
            LOGGER.debug("updateBrand failed:{}", pmsBrand);
        }
        return commonResult;
    }

    @ApiOperation("删除指定id的品牌")
    @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult deleteBrand(@PathVariable("id") Long id) {
        boolean result = brandService.removeById(id);
        if (result) {
            LOGGER.debug("deleteBrand success :id={}", id);
            return CommonResult.success(null);
        } else {
            LOGGER.debug("deleteBrand failed :id={}", id);
            return CommonResult.failed("操作失败");
        }
    }

    @ApiOperation("分页查询品牌列表")
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
                                                        @ApiParam("页码") Integer pageNum,
                                                        @RequestParam(value = "pageSize", defaultValue = "3")
                                                        @ApiParam("每页数量") Integer pageSize) {
        Page<PmsBrand> page = new Page<>(pageNum, pageSize);
        Page<PmsBrand> pageResult = brandService.page(page);
        return CommonResult.success(CommonPage.restPage(pageResult));
    }

    @ApiOperation("获取指定id的品牌详情")
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
        return CommonResult.success(brandService.getById(id));
    }

}

Everyone must remember to like, subscribe, and follow

Prevent it from being found next time

Your support is the driving force for me to continue to create! ! !

Guess you like

Origin blog.csdn.net/weixin_44302240/article/details/123542894