SpringBoot e-commerce project mall (40K + STAR) address: github.com/macrozheng/…
Abstract
I believe that the ORM framework used by many friends in the project is MyBatis. If you use MyBatis alone to operate the database, you need to write a lot of SQL queries for single tables by hand. At this time we often choose an enhancement tool to implement these single table CRUD operations, here recommend a good tool MyBatis-Plus!
MyBatis – Plus introduction
MyBatis-Plus (MP for short) is a MyBatis enhancement tool, on the basis of MyBatis only do enhancement do not change, to simplify the development and improve efficiency. MyBatis-Plus provides code generator, you can generate controller, service, mapper, model, mapper. XML code, at the same time provides rich CRUD operation methods, help us free hands!
MyBatis – Plus integration
First we need to integrate MyBatis-Plus in the SpringBoot project, and then we will explain how to use it in detail!
- in
pom.xml
To add related dependencies, mainly MyBatis-Plus, MyBatis-Plus Generator and Velocity template engine;
<dependencies>
<! - Mybatis - Plus depend on -- -- >
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2 rainfall distribution on 10-12</version>
</dependency>
<! --Mybatis -- Plus code generator -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.2 rainfall distribution on 10-12</version>
</dependency>
<! --Velocity template generation engine -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
</dependencies>
Copy the code
- Configuration file in SpringBoot
application.yml
Add the following configuration, 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 path
global-config:
db-config:
id-type: auto The global default primary key type is set to increment
configuration:
auto-mapping-behavior: partial Only non-nested ResultMaps are automatically mapped
map-underscore-to-camel-case: true Enable automatic hump naming rule mapping
Copy the code
- Add MyBatis-Plus Java configuration using
@MapperScan
Note Configure the Mapper interface path that needs to be scanned. MyBatis-Plus has its own paging function. Configure the paging plug-inPaginationInterceptor
.
/** * Created by macro on 2019/4/8. */
@Configuration
@MapperScan("com.macro.mall.tiny.modules.*.mapper")
public class MyBatisConfig {
@Bean
public PaginationInterceptor paginationInterceptor(a) {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
returnpaginationInterceptor; }}Copy the code
Code generator
MyBatis-Plus provides a code generator, you can one-click generation of controller, service, mapper, model, mapper.xml code, very convenient!
- First we create the code generator class
MyBatisPlusGenerator
, run it directlymain
Method to generate the relevant code;
/** * MyBatisPlus code generator * 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("Module name");
String[] tableNames = scanner("Table name, separated by multiple Commas").split(",");
// Code generator
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();
}
/** * Read the console contents */
private static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
System.out.println(("Please enter" + tip + ":"));
if (scanner.hasNext()) {
String next = scanner.next();
if (StrUtil.isNotEmpty(next)) {
returnnext; }}throw new MybatisPlusException("Please enter the correct one" + tip + "!");
}
/** * Initialize the global configuration */
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;
}
/** * Initializes data source configuration */
private static DataSourceConfig initDataSourceConfig(a) {
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;
}
/** * Initialize package configuration */
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;
}
/** * Initializes template configuration */
private static TemplateConfig initTemplateConfig(a) {
TemplateConfig templateConfig = new TemplateConfig();
// You can configure the Controller, service, and Entity templates
// The mapper. XML template needs to be configured separately
templateConfig.setXml(null);
return templateConfig;
}
/** * Initializes policy configuration */
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);
// Wildcard mode can be enabled when the table name contains asterisks
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;
}
/** * Initializes the custom configuration */
private static InjectionConfig initInjectionConfig(String projectPath, String moduleName) {
// Custom configuration
InjectionConfig injectionConfig = new InjectionConfig() {
@Override
public void initMap(a) {
// Can be used to customize attributes}};// The template engine is Velocity
String templatePath = "/templates/mapper.xml.vm";
// Customize the output configuration
List<FileOutConfig> focList = new ArrayList<>();
// Custom configurations are printed first
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// Customize the output file name. If your Entity has a prefix or suffix, note that the XML name will change accordingly!!
return projectPath + "/src/main/resources/mapper/" + moduleName
+ "/" + tableInfo.getEntityName() + "Mapper"+ StringPool.DOT_XML; }}); injectionConfig.setFileOutConfigList(focList);returninjectionConfig; }}Copy the code
- Then, in
resources
Add a configuration file to the directorygenerator.properties
, add the data source configuration of the code generator and the name of the base package where the business code is stored;
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
Copy the code
- Careful friends can find
MyBatisPlusGenerator
A lot of configuration code did not add annotations, in fact, MyBatis-Plus source code in Chinese annotations is very perfect, just view the source code can, here excerpt a paragraphDataSourceConfig
Source code;
/** * Database configuration **@author YangHu, hcl
* @since2016/8/30 * /
@Data
@Accessors(chain = true)
public class DataSourceConfig {
/** * database information */
private IDbQuery dbQuery;
/** * Database type */
private DbType dbType;
/** * PostgreSQL schemaName */
private String schemaName;
/** * Type conversion */
private ITypeConvert typeConvert;
/** * keyword processor *@since3.3.2 rainfall distribution on 10-12 * /
private IKeyWordsHandler keyWordsHandler;
/** * driver connection URL */
private String url;
/** * Driver name */
private String driverName;
/** * Database connection user name */
private String username;
/** * Database connection password */
private String password;
// omit some code......
}
Copy the code
- Code generators support two modes, one that generates single-table code, such as just generate
pms_brand
The table code can be entered firstpms
After the inputpms_brand
;
- Generate a list of single-table code structures;
- Another type of code that directly generates the entire module requires a wildcard
*
Such as generateums
The module code can be entered firstums
After the inputums_*
;
- Generate an overview of the entire module code structure.
Customize the generated template
MyBatis-Plus uses a templating engine to generate code, which supports the Velocity (default), Freemarker, and Beetl templating engine.
- First we can find the default template from the source of MyBatis-Plus Generator dependency package and copy it to the project
resources/templates
Directory;
- in
MyBatisPlusGenerator
Class forTemplateConfig
Configure the path of each template.
/** * MyBatisPlus code generator * Created by macro on 2020/8/20
public class MyBatisPlusGenerator {
/** * Initializes template configuration */
private static TemplateConfig initTemplateConfig(a) {
TemplateConfig templateConfig = new TemplateConfig();
// You can configure the Controller, service, and Entity templates
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");
// The mapper. XML template needs to be configured separately
templateConfig.setXml(null);
returntemplateConfig; }}Copy the code
- To customize the template, during the customization process we can find a lot of built-in variables for output to the template, here
service.java.vm
Templates are examples, such aspackage
,table
These variables;
package ${package.Service}; import ${package.Entity}.${entity}; import ${superServiceClassPackage}; /** * <p> * $! $${date} */ #if(${kotlin}) interface ${table.servicename} : ${superServiceClass}<${entity}> #else public interface ${table.serviceName} extends ${superServiceClass}<${entity}> { } #endCopy the code
- Knowing where these variables come from will help us customize our templates, because these variables are actually from
AbstractTemplateEngine
thegetObjectMap
Method, specific variable role can refer to the source code.
/** * Template engine abstract class **@author hubin
* @sinceThe 2018-01-10 * /
public abstract class AbstractTemplateEngine {
/** * Render object MAP information **@paramTableInfo Table information object *@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()));
returnObjects.isNull(config.getInjectionConfig()) ? objectMap : config.getInjectionConfig().prepareObjectMap(objectMap); }}Copy the code
CRUD operations
The power of MyBatis-Plus lies not only in its code generation capabilities, but also in its rich CRUD methods, allowing us to implement single-table CRUD with almost no hand-written SQL!
- That we generated before
PmsBrandMapper
Interface due to inheritanceBaseMapper
Interface, directly with various CRUD methods;
/** * <p> ** Mapper interface * </p> **@author macro
* @sinceThe 2020-08-20 * /
public interface PmsBrandMapper extends BaseMapper<PmsBrand> {}Copy the code
- Let’s see
BaseMapper
In the method, can basically meet our daily needs;
- That we generated before
PmsBrandService
Interface due to inheritanceIService
Interface, also has various CRUD methods;
/** ** <p> **@author macro
* @sinceThe 2020-08-20 * /
public interface PmsBrandService extends IService<PmsBrand> {}Copy the code
- You can look at the ratio
BaseMapper
More abundant in;
- With these
IService
andBaseMapper
Provide these methods, we single table query almost no handwritten SQL implementation, using MyBatis-Plus implementation beforePmsBrandController
The method is more relaxed!
/** ** <p> * brand table front-end controller * </p> **@author macro
* @sinceThe 2020-08-20 * /
@API (tags = "PmsBrandController", description = "Product Brand Management ")
@RestController
@RequestMapping("/brand")
public class PmsBrandController {
private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);
@Autowired
private PmsBrandService brandService;
@apiOperation (" Get all brand list ")
@RequestMapping(value = "/listAll", method = RequestMethod.GET)
@ResponseBody
public CommonResult<List<PmsBrand>> getBrandList() {
return CommonResult.success(brandService.list());
}
@apiOperation (" Add brand ")
@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("Operation failed");
LOGGER.debug("createBrand failed:{}", pmsBrand);
}
return commonResult;
}
@apiOperation (" Update the specified brand name ")
@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("Operation failed");
LOGGER.debug("updateBrand failed:{}", pmsBrand);
}
return commonResult;
}
@apiOperation (" delete brand with specified 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("Operation failed"); }}@apiOperation (" paging query brand list ")
@RequestMapping(value = "/list", method = RequestMethod.GET)
@ResponseBody
public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
@ ApiParam (" page ") Integer pageNum,
@RequestParam(value = "pageSize", defaultValue = "3")
@apiparam (" number per page ") Integer pageSize) {
Page<PmsBrand> page = new Page<>(pageNum, pageSize);
Page<PmsBrand> pageResult = brandService.page(page);
return CommonResult.success(CommonPage.restPage(pageResult));
}
@apiOperation (" Get brand details for specified ID ")
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
@ResponseBody
public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
returnCommonResult.success(brandService.getById(id)); }}Copy the code
Project source code address
Github.com/macrozheng/…
In this paper, making github.com/macrozheng/… Already included, welcome everyone Star!