电商生鲜网站开发(一)——Spring Boot项目开发准备
本系列内容完成Spring Boot框架的电商生鲜网站开发的完整案例,前后端分离开发的案例,先开发后端接口后开发前端,最后部署等待。
Spring Boot项目开发准备
文章目录
- Spring Boot项目开发准备
-
- IDEA web开发必备插件
- Postman安装——接口自测
- MySQL可视化工具——mac平台
- 数据库设计
- 项目初始化--依赖引入pom
- 自动创建实体类和mapper
- 配置项目的数据库连接
- log4j2日志的引入
- AOP统一处理Web请求日志
IDEA web开发必备插件
Maven Helper 与Maven一起工作的必备插件
Free Mybatis Tool 帮助我们跳转和识别Mapper中的一些语法错误
Postman安装——接口自测
帮助我们开发时接口自测的调试工具,免费
MySQL可视化工具——mac平台
Sequel pro for mac是Mac os平台上的一款帮助用户快速连接SQL数据库的开源免费Mac软件
http://sequelpro.com/test-builds
在连接MySQL时可能会出现问题:MySQL said: Authentication plugin ‘caching_sha2_password’ cannot be loaded
这会初始化数据库,数据库有数据的话请一定备份😭
打开系偏好设置-MySQL-Initialize Database-输入设置的密码-勾选Use Legacy Password Encryption-启动MySQL
数据库设计
案例:
/* Source Server : localhost Source Server Type : MySQL Source Server Version : 80026 Source Host : localhost:3306 Source Schema : fresh_mall Target Server Type : MySQL Target Server Version : 80026 File Encoding : 65001*/SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure for mall_cart-- ----------------------------DROP TABLE IF EXISTS `mall_cart`;CREATE TABLE `mall_cart` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '购物车id', `product_id` int NOT NULL COMMENT '商品id', `user_id` int NOT NULL COMMENT '用户id', `quantity` int NOT NULL DEFAULT '1' COMMENT '商品数量', `selected` int NOT NULL DEFAULT '1' COMMENT '是否已勾选:0代表未勾选,1代表已勾选', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='购物车';-- ------------------------------ Table structure for mall_category-- ----------------------------DROP TABLE IF EXISTS `mall_category`;CREATE TABLE `mall_category` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `name` varchar(32) NOT NULL DEFAULT '' COMMENT '分类目录名称', `type` int NOT NULL COMMENT '分类目录级别,例如1代表一级,2代表二级,3代表三级', `parent_id` int NOT NULL COMMENT '父id,也就是上一级目录的id,如果是一级目录,那么父id为0', `order_num` int NOT NULL COMMENT '目录展示时的排序', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品分类 ';-- ------------------------------ Table structure for mall_order-- ----------------------------DROP TABLE IF EXISTS `mall_order`;CREATE TABLE `mall_order` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', `order_no` varchar(128) NOT NULL DEFAULT '' COMMENT '订单号(非主键id)', `user_id` int NOT NULL COMMENT '用户id', `total_price` int NOT NULL COMMENT '订单总价格', `receiver_name` varchar(32) NOT NULL COMMENT '收货人姓名快照', `receiver_mobile` varchar(32) NOT NULL COMMENT '收货人手机号快照', `receiver_address` varchar(128) NOT NULL DEFAULT '' COMMENT '收货地址快照', `order_status` int NOT NULL DEFAULT '10' COMMENT '订单状态: 0用户已取消,10未付款(初始状态),20已付款,30已发货,40交易完成', `postage` int DEFAULT '0' COMMENT '运费,默认为0', `payment_type` int NOT NULL DEFAULT '1' COMMENT '支付类型,1-在线支付', `delivery_time` timestamp NULL DEFAULT NULL COMMENT '发货时间', `pay_time` timestamp NULL DEFAULT NULL COMMENT '支付时间', `end_time` timestamp NULL DEFAULT NULL COMMENT '交易完成时间', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='订单表;';-- ------------------------------ Table structure for mall_order_item-- ----------------------------DROP TABLE IF EXISTS `mall_order_item`;CREATE TABLE `mall_order_item` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键id', `order_no` varchar(128) NOT NULL DEFAULT '' COMMENT '归属订单id', `product_id` int NOT NULL COMMENT '商品id', `product_name` varchar(100) NOT NULL DEFAULT '' COMMENT '商品名称', `product_img` varchar(128) NOT NULL DEFAULT '' COMMENT '商品图片', `unit_price` int NOT NULL COMMENT '单价(下单时的快照)', `quantity` int NOT NULL DEFAULT '1' COMMENT '商品数量', `total_price` int NOT NULL DEFAULT '0' COMMENT '商品总价', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='订单的商品表 ';-- ------------------------------ Table structure for mall_product-- ----------------------------DROP TABLE IF EXISTS `mall_product`;CREATE TABLE `mall_product` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '商品主键id', `name` varchar(100) NOT NULL COMMENT '商品名称', `image` varchar(500) NOT NULL DEFAULT '' COMMENT '产品图片,相对路径地址', `detail` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT '' COMMENT '商品详情', `category_id` int NOT NULL COMMENT '分类id', `price` int NOT NULL COMMENT '价格,单位-分', `stock` int NOT NULL COMMENT '库存数量', `status` int NOT NULL DEFAULT '1' COMMENT '商品上架状态:0-下架,1-上架', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表';-- ------------------------------ Table structure for mall_user-- ----------------------------DROP TABLE IF EXISTS `mall_user`;CREATE TABLE `mall_user` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '用户id', `username` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名', `password` varchar(50) NOT NULL COMMENT '用户密码,MD5加密', `personalized_signature` varchar(50) NOT NULL DEFAULT '' COMMENT '个性签名', `role` int NOT NULL DEFAULT '1' COMMENT '角色,1-普通用户,2-管理员', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表 ';SET FOREIGN_KEY_CHECKS = 1;
项目初始化–依赖引入pom
pom.xml sprintboot版本2.2.1.RELEASE
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency><--插件配置--> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.7</version> <configuration> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin>
自动创建实体类和mapper
利用mybatis-generator自动创建实体类和mapper
resources下创建generatorConfig.xml
配置mysql-connector-java-8.0.18.jar在你电脑存放的路径,我这里都放入resources
配置数据库连接
配置生成Model类存放位置
配置生成mapper映射文件存放位置
配置生成Dao类存放位置
配置生成对应表及类名
<generatorConfiguration> <classPathEntry location="/Users/cat/Documents/fresh-mall/src/main/resources/mysql-connector-java-8.0.18.jar"/> <context id="MysqlTables" targetRuntime="MyBatis3"> <property name="autoDelimitKeywords" value="true"/> <property name="beginningDelimiter" value="`"/> <property name="endingDelimiter" value="`"/> <commentGenerator> <property name="suppressDate" value="true"/> <property name="suppressAllComments" value="true"/> </commentGenerator> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/fresh_mall?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull" userId="root" password="root1024"> <property name="nullCatalogMeansCurrent" value="true"/> </jdbcConnection> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <javaModelGenerator targetPackage="com.learn2333.freshmall.model.pojo" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> <property name="trimStrings" value="true"/> <property name="immutable" value="false"/> </javaModelGenerator> <sqlMapGenerator targetPackage="mappers" targetProject="src/main/resources"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <javaClientGenerator type="XMLMAPPER" targetPackage="com.learn2333.freshmall.model.dao" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <table schema="root" tableName="mall_cart" domainObjectName="Cart" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="mall_category" domainObjectName="Category" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="mall_order" domainObjectName="Order" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="mall_order_item" domainObjectName="OrderItem" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="mall_product" domainObjectName="Product" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> <table tableName="mall_user" domainObjectName="User" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"> </table> </context></generatorConfiguration>
点击右侧mavan中的插件–这个插件是因为上面pom中配置的才生效的
会自动生成mapper和实体类
在生成的mapper接口和mapper.xml文件中左侧行号右边会有互相跳转的箭头,这是配置的IDEA插件Free Mybatis Tool 生效了
配置项目的数据库连接
application.properties
server.port=8080spring.datasource.name=fresh_mallspring.datasource.url=jdbc:mysql://127.0.0.1:3306/fresh_mall?useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghaispring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.username=rootspring.datasource.password=root1024#mapper的位置mybatis.mapper-locations=classpath:mappers/*.xml
Application启动类配置dao mapper接口的注解路径
@MapperScan(basePackages = "com.learn2333.freshmall.model.dao")
创建测试类
controller.UserController
package com.learn2333.freshmall.controller;import com.learn2333.freshmall.model.pojo.User;import com.learn2333.freshmall.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.ResponseBody;/** * 用户控制器 */@Controllerpublic class UserController { @Autowired UserService userService; @GetMapping("/test") @ResponseBody public User personalPage() { return userService.getUser(); }}
service.UserService接口
package com.learn2333.freshmall.service;import com.learn2333.freshmall.model.pojo.User;public interface UserService { User getUser();}
service.impl.UserServiceImpl实现类
package com.learn2333.freshmall.service.impl;import com.learn2333.freshmall.model.dao.UserMapper;import com.learn2333.freshmall.model.pojo.User;import com.learn2333.freshmall.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Override public User getUser() { return userMapper.selectByPrimaryKey(1); }}
运行后访问test即可获得数据,连接成功
但是此时在代码中:引入的mapper会被标红,但仍可正常运行
@Autowired UserMapper userMapper;
这是因为编译器无法自动识别mapper,因为mapper是在springboot入口@MapperScan(basePackages = "com.learn2333.freshmall.model.dao")
配置的,这是为了给mybatis识别用的,而编译器无法识别,解决方案可以在dao下的mapper接口上增加注解@Repositor,这样IDEA就会认为@Autowired进来的mapper是一个资源。
log4j2日志的引入
日志级别:error, warn,info,debug,trace(依次向下级别递减)
排除之前的日志组件Logback依赖
pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions></dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId></dependency>
配置日志组件
log4j2.xml
配置日志文件保存的位置
<Configuration status="fatal"> <Properties> <Property name="baseDir" value="${sys:user.home}/logs"/> </Properties> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayoutpattern="[%d{MM:dd HH:mm:ss.SSS}] [%level] [%logger{36}] - %msg%n"/> </Console> <RollingFile name="debug_appender" fileName="${baseDir}/debug.log" filePattern="${baseDir}/debug_%i.log.%d{yyyy-MM-dd}"> <Filters> <ThresholdFilter level="debug"/> <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> <RollingFile name="info_appender" fileName="${baseDir}/info.log" filePattern="${baseDir}/info_%i.log.%d{yyyy-MM-dd}"> <Filters> <ThresholdFilter level="info"/> <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> <RollingFile name="error_appender" fileName="${baseDir}/error.log" filePattern="${baseDir}/error_%i.log.%d{yyyy-MM-dd}"> <Filters> <ThresholdFilter level="error"/> </Filters> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> </Appenders> <Loggers> <logger name="org.springframework" level="info" additivity="true"> <AppenderRef ref="Console" /> <AppenderRef ref="MyFile" /> </logger> <Root level="debug"> <AppenderRef ref="Console"/> <AppenderRef ref="debug_appender"/> <AppenderRef ref="info_appender"/> <AppenderRef ref="error_appender"/> </Root> </Loggers></Configuration>
运行项目,可以看到在配置好的文件路径下生成了日志
打开控制台使用tail -f 文件名
可以打印出文件新增加的内容
访问一个请求,内容也会相应变化,可以用这个方法调试程序。
AOP统一处理Web请求日志
是对系统健壮性的一个保护,利用fitter把每一个请求打印出来,提高开发和调试的效率
目的:创建fitter,把请求和响应信息打印出来
pom引入aop
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
fitter.WebLogAspect.java 直接创建fitter类,利用注解配合方法实现自定义请求和响应日志的输出
package com.learn2333.freshmall.fitter;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.*;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.util.Arrays;/** * 打印请求和响应信息 */@Aspect@Componentpublic class WebLogAspect { private final Logger log = LoggerFactory.getLogger(WebLogAspect.class); //指定拦截点 @Pointcut("execution(public * com.learn2333.freshmall.controller.*.*(..))") public void webLog() { } @Before("webLog()") public void doBefore(JoinPoint joinPoint) { //收到请求,记录请求内容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //记录日志 //url log.info("URL : " + request.getRequestURI().toString()); //请求类型 log.info("HTTP_METHOD : " + request.getMethod()); //ip log.info("IP : " + request.getRemoteAddr()); //类的方法 log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); //参数 log.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); } @AfterReturning(returning = "res", pointcut = "webLog()") public void doAfterReturn(Object res) throws JsonProcessingException { //处理完请求,返回内容 //writeValueAsString是fastjson提供的一个将对象转成json格式的工具 log.info("RESPONSE : " + new ObjectMapper().writeValueAsString(res)); }}