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"
"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"
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
@MapperScan
annotations 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-inPaginationInterceptor
.
/**
* 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 itsmain
methods 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
resources
add a configuration file in the directory, addgenerator.properties
the 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
MyBatisPlusGenerator
that 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 excerptDataSourceConfig
of 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_brand
table code can be input firstpms
, and then inputpms_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 theums
module code, you can enter it firstums
and then enter itums_*
;
- 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/templates
directory;
- Configure it in the
MyBatisPlusGenerator
classTemplateConfig
and 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.vm
template is used as an example, for examplepackage
,table
these 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}> {
}
#end
Copy 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
AbstractTemplateEngine
methods . ForgetObjectMap
specific 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
PmsBrandMapper
because it inherits theBaseMapper
interface;
/**
* <p>
* 品牌表 Mapper 接口
* </p>
*
* @author macro
* @since 2020-08-20
*/
public interface PmsBrandMapper extends BaseMapper<PmsBrand> {
}
- Let's see if
BaseMapper
the method in the following can basically meet our daily needs;
- The interface we generated earlier also has various CRUD methods
PmsBrandService
because it inherits theIService
interface;
/**
* <p>
* 品牌表 服务类
* </p>
*
* @author macro
* @since 2020-08-20
*/
public interface PmsBrandService extends IService<PmsBrand> {
}
- It can be seen that the ratio
BaseMapper
is more abundant;
- With the methods provided in these
IService
andBaseMapper
in, we almost do not need to write SQL to implement single-table queries, and itPmsBrandController
is 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! ! !