写在前面 本文介绍如何通过SpringBoot+Mybatis来搭建一个电商系统的基本骨架,并以商品品牌管理为例来实现基本的CRUD操作,以及通过PageHelper实现分页查询。
使用的框架简介 SpringBoot 一个基于Spring的快速搭建Java企业级应用的开发框架。
Mybatis generator Mybatis代码生成器,可根据数据库生成对应的model、mapper.xml、mapper接口以及Example,这样一般的单表查询不用再手写mapper。
MyBatis分页插件,只需几行简单事务代码就能实现分页功能。如果你使用的是SpringBoot,那么只要整合了PagerHelper就自动整合了MyBatis:
1 2 3 4 5 PageHelper.startPage(pageNum, pageSize); //之后进行查询操作将自动进行分页 List<PmsBrand> brandList = brandMapper.selectByExample(new PmsBrandExample()); //通过构造PageInfo对象获取分页信息,如当前页码,总页数,总条数 PageInfo<PmsBrand> pageInfo = new PageInfo<PmsBrand>(list);
Druid 阿里巴巴开源的数据库连接池,性能极佳,提供了多种特性和功能。
项目初始化 第一步 ,使用Maven新建一个名为myshop-all的项目:
第二步 ,删除其中的src目录,并修改其中的pom文件为如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kenbings.shop</groupId> <artifactId>myshop-all</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <modules> <module>shop-basic</module> </modules> </project>
第三步 ,在myshop-all目录下,使用spring Initializr
构建工具构建一个SpringBoot的Web应用,名称为shop-basic的Module:
第四步 ,修改shop-basic
这一web项目的pom文件内容为如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.kenbings.shop</groupId> <artifactId>shop-basic</artifactId> <version>0.0.1-SNAPSHOT</version> <name>shop-basic</name> <description>项目骨架搭建</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--SpringBoot通用依赖模块--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--MyBatis分页插件--> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.10</version> </dependency> <!--集成druid连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!-- MyBatis 生成器 --> <dependency> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-core</artifactId> <version>1.3.3</version> </dependency> <!--Mysql数据库驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
第五步 ,修改shop-basic
这一web项目的application.yml配置文件为如下所示:
1 2 3 4 5 6 7 8 9 10 11 server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username: root password: envy123 mybatis: mapper-locations: - classpath:mapper/*.xml - classpath*:com/**/mapper/*.xml
第六步 ,新建com.kenbings.shop.shopbasic.common.api
这两级目录,然后在api目录中新建IErrorCode接口:
1 2 3 4 5 6 7 8 /** * 封装API的错误码 */ public interface IErrorCode { long getCode(); String getMessage(); }
新建ResultCode枚举类,需要实现之前的IErrorCode接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 /** * 一些常用API操作码枚举类 */ public enum ResultCode implements IErrorCode{ SUCCESS(200, "操作成功"), FAILED(500, "操作失败"), VALIDATE_FAILED(404, "参数检验失败"), UNAUTHORIZED(401, "暂未登录或token已经过期"), FORBIDDEN(403, "没有相关权限"); private long code; private String message; private ResultCode(long code, String message){ this.code = code; this.message = message; } @Override public long getCode() { return code; } @Override public String getMessage() { return message; } }
新建CommonResult类,用于构建一个通用的返回对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 /** * 通用返回对象 */ public class CommonResult<T> { private long code; private String message; private T data; public CommonResult(){} public CommonResult(long code,String message,T data){ this.code = code; this.message = message; this.data = data; } /** * 成功返回数据 * @param data 返回的数据 */ public static <T> CommonResult<T> success(T data){ return new CommonResult<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(),data); } /** * 成功返回数据 * @param data 返回的数据 */ public static <T> CommonResult<T> success(T data,String message){ return new CommonResult<>(ResultCode.SUCCESS.getCode(), message,data); } /** * 失败返回数据 * @param errorCode 返回的错误码 */ public static <T> CommonResult<T> failed(IErrorCode errorCode){ return new CommonResult<>(errorCode.getCode(),errorCode.getMessage(),null); } /** * 失败返回数据 * @param message 返回的提示信息 */ public static <T> CommonResult<T> failed(String message){ return new CommonResult<>(ResultCode.FAILED.getCode(), message,null); } /** * 失败返回结果 */ public static <T> CommonResult<T> failed() { return failed(ResultCode.FAILED); } /** * 参数验证失败返回结果 */ public static <T> CommonResult<T> validateFailed() { return failed(ResultCode.VALIDATE_FAILED); } /** * 参数验证失败返回结果 * @param message 提示信息 */ public static <T> CommonResult<T> validateFailed(String message) { return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null); } /** * 未登录返回结果 */ public static <T> CommonResult<T> unauthorized(T data) { return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data); } /** * 未授权返回结果 */ public static <T> CommonResult<T> forbidden(T data) { return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data); } public long getCode() { return code; } public void setCode(long code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
新建CommonPage类,这是一个用于封装分页数据的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 /** * 分页数据封装类 */ public class CommonPage<T> { private Integer pageNum; private Integer pageSize; private Integer totalPage; private Long total; private List<T> list; /** * 将 PageHelper 分页后的list转换为分页信息 */ public static <T> CommonPage<T> restPage(List<T> list){ CommonPage<T> commonPage = new CommonPage<>(); PageInfo<T> pageInfo = new PageInfo<>(list); commonPage.setPageNum(pageInfo.getPageNum()); commonPage.setPageSize(pageInfo.getPageSize()); commonPage.setTotalPage(pageInfo.getPages()); commonPage.setTotal(pageInfo.getTotal()); commonPage.setList(pageInfo.getList()); return commonPage; } public Integer getPageNum() { return pageNum; } public void setPageNum(Integer pageNum) { this.pageNum = pageNum; } public Integer getPageSize() { return pageSize; } public void setPageSize(Integer pageSize) { this.pageSize = pageSize; } public Integer getTotalPage() { return totalPage; } public void setTotalPage(Integer totalPage) { this.totalPage = totalPage; } public List<T> getList() { return list; } public void setList(List<T> list) { this.list = list; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } }
第七步 ,新建com.kenbings.shop.shopbasic.config
目录,在里面定义一个名为MyBatisConfig的类,用于设置动态生成的mapper接口的路径信息:
1 2 3 4 5 6 7 /** * MyBatis配置类 */ @Configuration @MapperScan("com.kenbings.shop.shopbasic.mbg.mapper") public class MyBatisConfig { }
第八步 ,新建com.kenbings.shop.shopbasic.mbg
目录,在里面定义一个名为CommentGenerator的类,它需要继承DefaultCommentGenerator类,用于自定义注释生成器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 /** * 自定义注释生成器 */ public class CommentGenerator extends DefaultCommentGenerator { private boolean addRemarkComments = false; /** *设置用户配置的参数 */ @Override public void addConfigurationProperties(Properties properties) { super.addConfigurationProperties(properties); this.addRemarkComments = StringUtility.isTrue(properties.getProperty("addRemarkComments")); } /** * 给字段添加注释 */ @Override public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) { String remarks = introspectedColumn.getRemarks(); //根据参数和备注信息来判断是否添加备注信息 if (addRemarkComments && StringUtility.stringHasValue(remarks)) { addFieldJavaDoc(field, remarks); } } /** * 给model的字段添加注释 */ private void addFieldJavaDoc(Field field, String remarks) { //文档注释开始 field.addJavaDocLine("/**"); //获取数据库字段的备注信息 String[] remarkLines = remarks.split(System.getProperty("line.separator")); for (String remarkLine : remarkLines) { field.addJavaDocLine(" * " + remarkLine); } addJavadocTag(field, false); //文档注释结束 field.addJavaDocLine(" */"); } }
第九步 ,在mbg目录下新建一个名为Generator的类,用于执行MBG的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 /** * 用于执行MBG的代码 */ public class Generator { public static void main(String[] args) throws Exception { // MBG 执行过程中的警告信息 List<String> warnings = new ArrayList<String>(); //当生成的代码重复时,覆盖原代码 boolean overwrite = true; //读取我们的 MBG 配置文件 InputStream inputStream = Generator.class.getResourceAsStream("/generatorConfig.xml"); ConfigurationParser configurationParser = new ConfigurationParser(warnings); Configuration config = configurationParser.parseConfiguration(inputStream); inputStream.close(); DefaultShellCallback callback = new DefaultShellCallback(overwrite); //创建 MBG MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings); //执行生成代码 myBatisGenerator.generate(null); //输出警告信息 for (String warning : warnings) { System.out.println(warning); } } }
第十步 ,在resources目录下新建一个名为generatorConfig.xml
的配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"> <generatorConfiguration> <properties resource="generator.properties"/> <context id="MySqlContext" targetRuntime="MyBatis3" defaultModelType="flat"> <property name="beginningDelimiter" value="`"/> <property name="endingDelimiter" value="`"/> <property name="javaFileEncoding" value="UTF-8"/> <!-- 为模型生成序列化方法--> <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/> <!-- 为生成的Java模型创建一个toString方法 --> <plugin type="org.mybatis.generator.plugins.ToStringPlugin"/> <!--可以自定义生成model的代码注释--> <commentGenerator type="com.kenbings.shop.shopbasic.mbg.CommentGenerator"> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true"/> <property name="suppressDate" value="true"/> <property name="addRemarkComments" value="true"/> </commentGenerator> <!--配置数据库连接--> <jdbcConnection driverClass="${jdbc.driverClass}" connectionURL="${jdbc.connectionURL}" userId="${jdbc.userId}" password="${jdbc.password}"> <!--解决mysql驱动升级到8.0后不生成指定数据库代码的问题--> <property name="nullCatalogMeansCurrent" value="true" /> </jdbcConnection> <!--指定生成model的路径--> <javaModelGenerator targetPackage="com.kenbings.shop.shopbasic.mbg.model" targetProject="shop-basic\src\main\java"/> <!--指定生成mapper.xml的路径--> <sqlMapGenerator targetPackage="com.kenbings.shop.shopbasic.mbg.mapper" targetProject="shop-basic\src\main\resources"/> <!--指定生成mapper接口的的路径--> <javaClientGenerator type="XMLMAPPER" targetPackage="com.kenbings.shop.shopbasic.mbg.mapper" targetProject="shop-basic\src\main\java"/> <!--生成全部表tableName设为%--> <table tableName="pms_brand"> <generatedKey column="id" sqlStatement="MySql" identity="true"/> </table> </context> </generatorConfiguration>
第十一步 ,在resources目录下新建一个名为generator.properties
的数据配置文件:
1 2 3 4 jdbc.driverClass=com.mysql.cj.jdbc.Driver jdbc.connectionURL=jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai jdbc.userId=root jdbc.password=envy123
第十二步 ,打开数据库,执行如下SQL语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 DROP DATABASE IF EXISTS shop; CREATE DATABASE shop; USE shop; DROP TABLE IF EXISTS pms_brand; CREATE TABLE `pms_brand` ( `id` bigint NOT NULL AUTO_INCREMENT, `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `first_letter` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '首字母', `sort` int DEFAULT NULL, `factory_status` int DEFAULT NULL COMMENT '是否为品牌制造商:0->不是;1->是', `show_status` int DEFAULT NULL, `product_count` int DEFAULT NULL COMMENT '产品数量', `product_comment_count` int DEFAULT NULL COMMENT '产品评论数量', `logo` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '品牌logo', `big_pic` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '专区大图', `brand_story` text CHARACTER SET utf8 COLLATE utf8_general_ci COMMENT '品牌故事', PRIMARY KEY (`id`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=59 DEFAULT CHARSET=utf8mb3 COMMENT='品牌表';
此时我们项目的基本骨架如下所示:
第十三步 ,执行Generator类中的main方法,可以看到此时生成后的目录结构如下所示:
第十四步 ,新建com.kenbings.shop.shopbasic.service
目录,并在里面新建一个名为PmsBrandService的接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public interface PmsBrandService { /** * 查询所有的品牌信息 */ List<PmsBrand> listAllBrand(); /** * 创建一个新品牌 */ int createBrand(PmsBrand pmsBrand); /** * 修改一个品牌信息 */ int updateBrand(Long id,PmsBrand pmsBrand); /** * 删除一个品牌 */ int deleteBrand(Long id); /** * 分页查询品牌信息 */ List<PmsBrand> listBrand(int pageNum, int pageSize); /** * 获取某个品牌信息 */ PmsBrand getBrand(Long id); }
第十五步 ,在之前的service目录中新建一个名为impl的目录,然后在impl目录中新建一个名为PmsBrandServiceImpl的类,注意这个类需要实现PmsBrandService接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Service public class PmsBrandServiceImpl implements PmsBrandService { @Autowired private PmsBrandMapper pmsBrandMapper; @Override public List<PmsBrand> listAllBrand() { return pmsBrandMapper.selectByExample(new PmsBrandExample()); } @Override public int createBrand(PmsBrand pmsBrand) { return pmsBrandMapper.insertSelective(pmsBrand); } @Override public int updateBrand(Long id, PmsBrand pmsBrand) { pmsBrand.setId(id); return pmsBrandMapper.updateByPrimaryKeySelective(pmsBrand); } @Override public int deleteBrand(Long id) { return pmsBrandMapper.deleteByPrimaryKey(id); } @Override public List<PmsBrand> listBrand(int pageNum, int pageSize) { PageHelper.startPage(pageNum,pageSize); return pmsBrandMapper.selectByExample(new PmsBrandExample()); } @Override public PmsBrand getBrand(Long id) { return pmsBrandMapper.selectByPrimaryKey(id); } }
第十六步 ,新建com.kenbings.shop.shopbasic.controller
目录,并在里面新建一个名为PmsBrandController的类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 /** * 品牌管理Controller */ @RestController @RequestMapping("/brand") public class PmsBrandController { @Autowired private PmsBrandService pmsBrandService; private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class); /** * 查询所有的品牌信息 */ @GetMapping("/listAll") public CommonResult<List<PmsBrand>> getBrandList(){ return CommonResult.success(pmsBrandService.listAllBrand()); } /** * 创建一个新品牌 */ @PostMapping("/create") public CommonResult createBrand(@RequestBody PmsBrand pmsBrand){ CommonResult commonResult; int count = pmsBrandService.createBrand(pmsBrand); if(count==1){ commonResult = CommonResult.success(pmsBrand); LOGGER.debug("品牌创建成功:{}",pmsBrand); }else{ commonResult = CommonResult.failed("品牌创建失败"); LOGGER.debug("品牌创建失败:{}",pmsBrand); } return commonResult; } /** * 修改一个品牌信息 */ @PostMapping("/update/{id}") public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDTO, BindingResult result){ CommonResult commonResult; if(result.hasErrors()){ commonResult = CommonResult.failed(result.getFieldError().getDefaultMessage()); return commonResult; } int count = pmsBrandService.updateBrand(id,pmsBrandDTO); if(count==1){ commonResult = CommonResult.success(pmsBrandDTO); LOGGER.debug("品牌修改成功:{}",pmsBrandDTO); }else{ commonResult = CommonResult.failed("品牌修改失败"); LOGGER.debug("品牌修改失败:{}",pmsBrandDTO); } return commonResult; } /** * 删除一个品牌 */ @PostMapping("/delete/{id}") public CommonResult deleteBrand(@PathVariable("id") Long id){ CommonResult commonResult; int count = pmsBrandService.deleteBrand(id); if(count==1){ commonResult = CommonResult.success(null); LOGGER.debug("品牌删除成功:{}",id); }else{ commonResult = CommonResult.failed("品牌修改失败"); LOGGER.debug("品牌删除失败:{}",id); } return commonResult; } /** * 分页查询品牌信息 */ @GetMapping("/list") public CommonResult<CommonPage<PmsBrand>> listBrand( @RequestParam(value = "pageNum",defaultValue = "1")Integer pageNum, @RequestParam(value = "pageSize",defaultValue = "5")Integer pageSize ){ List<PmsBrand> pmsBrands = pmsBrandService.listBrand(pageNum, pageSize); return CommonResult.success(CommonPage.restPage(pmsBrands)); } /** * 获取某个品牌信息 */ @GetMapping("/{id}") public CommonResult<PmsBrand> getBrand(@PathVariable("id")Long id){ return CommonResult.success(pmsBrandService.getBrand(id)); }
第十七步 ,启动项目入口类ShopBasicApplication,打开浏览器进行测试,这里以访问id为1的品牌为例进行说明。
在浏览器地址栏中输入http://localhost:8080/brand/1
,可以看到浏览器显示如下信息:
这样关于项目的骨架搭建就完成了,后续学习如何接入Swagger-UI实现API文档的在线使用。本篇笔记源码,可以点击 这里 进行阅读。