E商城(后端)
1 项目准备
1.1 原型
1.1.1 后台管理
1.1.2 易购商城
1.2 功能结构
1.2.1 易购商城
- 首页
- 专题列表、专题详情
- 分类列表、分类详情
- 品牌列表、品牌详情
- 新品首发、人气推荐
- 优惠券列表、优惠券选择
- 团购
- 搜索
- 商品详情、商品评价、商品分享
- 购物车
- 下单
- 订单列表、订单详情、订单售后
- 地址、收藏、足迹、意见反馈
- 客服
1.2.2 后台管理
首页
会员管理
- 商城管理
- 商品管理
- 推广管理
- 系统管理
- 配置管理
- 统计报表
1.3 数据库设计
- 新建easy_mall数据库
- 运行sql文件
- 先执行easymall_table.sql
- 再执行easymall_data.sql
1.4 项目搭建
1.4.1 maven配置
1.4.2 SpringBoot项目创建
新建Spring initializer项目,选择SDK
输入maven相关信息
选择springboot版本和依赖
最后输入工程名和工程存储目录,点击“Finish”完成。
1.4.3 SpringBoot工程分析
1.4.3.1 简介
SpringBoot框架是一个快速搭建Spring项目的脚手架.
- starter启动器依赖
- 自动配置: 自动配置整合的其他框架或者第三方库
1.4.3.2 Maven pom.xml
maven是一个java的构建工具.
项目构建
依赖管理: 自动管理项目需要依赖的其他java包
在pom.xml中可以添加依赖:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.10</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
1.4.3.3 启动类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
1.4.3.4 配置文件
application.properties
2 接口开发基本步骤
2.1 开发第一个后端API接口
开发后端API接口可以选择Servlet,也可以直接使用SpringMVC框架.
2.1.1 SpringMVC框架
SpringMVC框架是一个web层的MVC框架,可以用来开发Web API接口.
第一步: 导入SpringMVC框架的依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
第二步:开发API接口
创建一个Controller控制器类: 控制器类中就包含多个API接口
Controller类是一个spring bean组件:一定要放在main主类所在的包下.
@RestController//标注这个类是一个Controller控制器 public class AdminIndexController { }
在Controller类中添加控制器方法,每个控制器方法就是一个API接口
@RequestMapping("/admin/user/count")//标注这个方法是一个API接口方法,并指定访问路径 public Object selectAdminUserCount(){ return null; }
@RequestMapping
这个注解可以自己指定支持的请求方式
@GetMapping
这个注解只支持Get请求方式
@PostMapping
这个注解只支持Post请求方式
@PutMapping
这个注解只支持Put请求方式
@DeleteMapping
这个注解只支持Delete请求方式
等等
2.1.2 Mybatis框架
Mybatis框架是一个持久层的orm框架,可以用来连接访问数据库.
依赖
mybatis
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency>
mysql
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
datasource
- 可以使用spring自带的,就不需要再导入数据源依赖
- 也可以使用其他数据源
- c3p0
- dbcp
- druid
- 等等
- c3p0
配置
springboot项目的配置都在resources目录下的application.properties文件:
数据源
# 数据源 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/easy_mall?serverTimezone=PRC spring.datasource.username=root spring.datasource.password=123456
mybatis
扫描Mapper映射文件
mybatis
mybatis.mapper-locations=classpath:/mapper/*Mapper.xml
mapper接口
定义Mapper接口,mapper接口定义用来执行sql语句的方法.
public interface AdminUserMapper { //每个方法对应一个sql语句 public int selectAdminUserCount(); }
mapper映射文件
mapper映射文件是一个xml文件,里边定义sql语句.
建议放在resources目录下(可以新建一个目录)。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace必须是对应的Mapper接口的全名(包名.接口名) --> <mapper namespace="com.edu.mall.admin.mapper.AdminUserMapper"> <!-- id必须是对应的方法的方法名 --> <select id="selectAdminUserCount" resultType="int"> select count(id) from litemall_admin </select> </mapper>
- namespace
- id
MapperScan
在主类上通过MapperScan注解扫描Mapper接口
@MapperScan("com.edu.mall.admin.mapper")
2.1.3 Controller调用Mapper
只要调用Mapper接口中的方法,就会执行对应的sql语句.
@Autowired//1.注入AdminUserMapper对象
AdminUserMapper adminUserMapper;
@RequestMapping("/admin/user/count")
public Object selectAdminUserCount(){
int count = adminUserMapper.selectAdminUserCount();//2.调用方法
Map<String,Object> result = new HashMap<>();
result.put("errno",0);
result.put("errmsg","成功");
result.put("data","管理员用户数量:"+count);
return result;
}
2.2 启动后端服务
后端springboot项目中直接内嵌了tomcat应用服务器,直接运行main主类,就可以启动tomcat并部署项目.
2.3 接口测试工具
- 浏览器 (只能发送get请求)
- 浏览器中安装插件
- 专业的工具
- postman
- ApiPost
- curl
- 等等
2.4 项目目录结构
src
main
java(java源码文件)
com.edu.mall.admin(基本包)
Application.java(主类)
controller
service
mapper
domain(po/pojo/model) : 跟数据库表映射的java实体类
vo : 主要是适配接口响应结果的java类
dto :数据传输对象,比如接收前端传递过来的参数对象
util : 工具类
common : 与业务功能无关的通用对象
config : 配置对象
resources(资源文件)
application.properties (配置文件)
mapper : Mapper映射文件
static : 静态资源(img/css/js/font)
templates : 模板文件(freemark/velocity/thymeleaf)
test(测试)
java
resources
pom.xml
2.5 项目三层架构
web层 (controller)
只负责跟前端交互,接收请求,响应结果.
业务层 (service)
负责处理业务逻辑.
持久层 (Dao/Mapper)
只负责执行sql,访问数据库中的数据.
三层的关系:
下层给给上层提供服务
web层给前端提供接口服务;
业务层给web层提供业务服务;
持久层给业务层提供数据服务.
上层依赖下层
上层需要调用下层的方法完成需要的功能
3 接口开发
管理端
3.1 首页仪表盘
@RestController
@RequestMapping("/admin/dashboard")
public class AdminDashbordController {
private final Log logger = LogFactory.getLog(AdminDashbordController.class);
@Autowired
private MallUserService userService;
@Autowired
private MallGoodsService goodsService;
@Autowired
private MallGoodsProductService productService;
@Autowired
private MallOrderService orderService;
@GetMapping("")
public Object info() {
int userTotal = userService.count();
int goodsTotal = goodsService.count();
int productTotal = productService.count();
int orderTotal = orderService.count();
Map<String, Integer> data = new HashMap<>();
data.put("userTotal", userTotal);
data.put("goodsTotal", goodsTotal);
data.put("productTotal", productTotal);
data.put("orderTotal", orderTotal);
return ResponseUtil.ok(data);
}
}
3.2 商品类目管理
@RestController
@RequestMapping("/admin/category")
public class AdminCategoryController {
private final Log logger = LogFactory.getLog(AdminCategoryController.class);
@Autowired
private LitemallCategoryService categoryService;
@GetMapping("/list")
public Object list() {
List<CategoryVo> categoryVoList = new ArrayList<>();
List<LitemallCategory> categoryList = categoryService.queryByPid(0);
for (LitemallCategory category : categoryList) {
CategoryVo categoryVO = new CategoryVo();
categoryVO.setId(category.getId());
categoryVO.setDesc(category.getDesc());
categoryVO.setIconUrl(category.getIconUrl());
categoryVO.setPicUrl(category.getPicUrl());
categoryVO.setKeywords(category.getKeywords());
categoryVO.setName(category.getName());
categoryVO.setLevel(category.getLevel());
List<CategoryVo> children = new ArrayList<>();
List<LitemallCategory> subCategoryList = categoryService.queryByPid(category.getId());
for (LitemallCategory subCategory : subCategoryList) {
CategoryVo subCategoryVo = new CategoryVo();
subCategoryVo.setId(subCategory.getId());
subCategoryVo.setDesc(subCategory.getDesc());
subCategoryVo.setIconUrl(subCategory.getIconUrl());
subCategoryVo.setPicUrl(subCategory.getPicUrl());
subCategoryVo.setKeywords(subCategory.getKeywords());
subCategoryVo.setName(subCategory.getName());
subCategoryVo.setLevel(subCategory.getLevel());
children.add(subCategoryVo);
}
categoryVO.setChildren(children);
categoryVoList.add(categoryVO);
}
return ResponseUtil.okList(categoryVoList);
}
private Object validate(LitemallCategory category) {
String name = category.getName();
if (StringUtils.isEmpty(name)) {
return ResponseUtil.badArgument();
}
String level = category.getLevel();
if (StringUtils.isEmpty(level)) {
return ResponseUtil.badArgument();
}
if (!level.equals("L1") && !level.equals("L2")) {
return ResponseUtil.badArgumentValue();
}
Integer pid = category.getPid();
if (level.equals("L2") && (pid == null)) {
return ResponseUtil.badArgument();
}
return null;
}
@PostMapping("/create")
public Object create(@RequestBody LitemallCategory category) {
Object error = validate(category);
if (error != null) {
return error;
}
categoryService.add(category);
return ResponseUtil.ok(category);
}
@GetMapping("/read")
public Object read(@NotNull Integer id) {
LitemallCategory category = categoryService.findById(id);
return ResponseUtil.ok(category);
}
@PostMapping("/update")
public Object update(@RequestBody LitemallCategory category) {
Object error = validate(category);
if (error != null) {
return error;
}
if (categoryService.updateById(category) == 0) {
return ResponseUtil.updatedDataFailed();
}
return ResponseUtil.ok();
}
@PostMapping("/delete")
public Object delete(@RequestBody LitemallCategory category) {
Integer id = category.getId();
if (id == null) {
return ResponseUtil.badArgument();
}
categoryService.deleteById(id);
return ResponseUtil.ok();
}
@GetMapping("/l1")
public Object catL1() {
// 所有一级分类目录
List<LitemallCategory> l1CatList = categoryService.queryL1();
List<Map<String, Object>> data = new ArrayList<>(l1CatList.size());
for (LitemallCategory category : l1CatList) {
Map<String, Object> d = new HashMap<>(2);
d.put("value", category.getId());
d.put("label", category.getName());
data.add(d);
}
return ResponseUtil.okList(data);
}
}
3.3 商品管理
@RestController
@RequestMapping("/admin/goods")
public class MallGoodsController {
@Autowired
private MallGoodsService mallGoodsService;
@Autowired
private MallCategoryService mallCategoryService;
@Autowired
private MallBrandService mallBrandService;
@GetMapping("/list")
public Object findGoodsList(String name, Long goodsId, String goodsSn,
String sort, String order,
@RequestParam(value="page",required = false,defaultValue = "1") Integer page, @RequestParam(value="limit",required = false,defaultValue = "20")Integer limit){
PageHelper.startPage(page, limit);
List<MallGoods> goodsList = mallGoodsService.findGoodsList(name, goodsId, goodsSn, sort, order);
PageInfo<MallGoods> pageInfo = new PageInfo<>(goodsList);
return ResponseUtils.okPage(pageInfo);
}
@GetMapping("/catAndBrand")
public Object findCategoryAndBrandList(){
//查询所有一级分类
List<MallCategory> level1List = mallCategoryService.findCategoryListByParentId(0l);
//循环查询一级分类下的二级分类
for (MallCategory l1 : level1List){
l1.setChildren(mallCategoryService.findCategoryListByParentId(l1.getId()));
}
//转换为VO
List<SelectVo> level1VoList = new ArrayList<>();
for (MallCategory l1 : level1List){
SelectVo l1Vo = new SelectVo();
l1Vo.setValue(l1.getId()+"");
l1Vo.setLabel(l1.getName());
List<SelectVo> children = new ArrayList<>();
for(MallCategory l2 : l1.getChildren()){
SelectVo l2Vo = new SelectVo();
l2Vo.setValue(l2.getId()+"");
l2Vo.setLabel(l2.getName());
children.add(l2Vo);
}
l1Vo.setChildren(children);
level1VoList.add(l1Vo);
}
//查询所有品牌
List<MallBrand> allBrands = mallBrandService.findAllBrands();
//转换为VO
List<SelectVo> brandVoList = new ArrayList<>();
for (MallBrand b : allBrands){
SelectVo bVo = new SelectVo();
bVo.setValue(b.getId()+"");
bVo.setLabel(b.getName());
brandVoList.add(bVo);
}
Map<String,Object> data = new HashMap<>();
data.put("categoryList", level1VoList);
data.put("brandList", brandVoList);
return ResponseUtils.ok(data);
}
@PostMapping("/create")
public Object createGoods(@RequestBody GoodsProductDto goodsProductDto){
mallGoodsService.createGoods(goodsProductDto);
return ResponseUtils.ok();
}
@PostMapping("/delete")
public Object delGoods(@RequestBody MallGoods mallGoods){
mallGoodsService.delGoods(mallGoods.getId());
return ResponseUtils.ok();
}
@GetMapping("/detail")
public Object findGoodsDetails(Long id){
GoodsProductCategoryDto goodsDetails = mallGoodsService.findGoodsDetails(id);
return ResponseUtils.ok(goodsDetails);
}
@PostMapping("/update")
public Object updateGoods(@RequestBody GoodsProductDto productDto){
mallGoodsService.updateGoods(productDto);
return ResponseUtils.ok();
}
}
3.4 登录
@RestController
public class AdminUserController {
@Autowired
private AdminUserService adminUserService;
@PostMapping("/admin/auth/login")
public Object login(@RequestBody AdminUser user){
AdminUser login = adminUserService.login(user.getUsername(), user.getPassword());
if(login!=null){
Map<String,Object> data = new HashMap<>();
data.put("token", JwtHelper.createToken(login.getId()));
Map<String,Object> adminInfo = new HashMap<>();
adminInfo.put("nickName",login.getUsername());
adminInfo.put("avatar", login.getAvatar());
data.put("adminInfo", adminInfo );
return ResponseUtils.ok(data);
}else{
return ResponseUtils.fail(ResponseUtils.INVALID_USER, ResponseUtils.INVALID_LOGIN);
}
}
@GetMapping("/admin/auth/info")
public Object info(){
Map<String,Object> data = new HashMap<>();
data.put("roles",new String[]{"超级管理员"});
data.put("name","admin123");
data.put("perms", new String[]{"*"});
data.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
return ResponseUtils.ok(data);
}
}
移动端
3.5 首页
package com.edu.mall.mobile.controller;
import com.edu.mall.mobile.utils.ResponseUtils;
import com.edu.mall.service.MobileHomeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/wx/home")
public class HomeController {
//注入MobileHomeService
@Autowired
private MobileHomeService mobileHomeService;
@RequestMapping("/index")
public Object index(){
Object data = mobileHomeService.getHomeIndex();
return ResponseUtils.ok(data);
}
}
@Service
public class MobileHomeServiceImpl implements MobileHomeService {
//注入LitemallAdMapper
@Autowired
private LitemallAdMapper litemallAdMapper;
//注入LitemallCategoryMapper
@Autowired
private LitemallCategoryMapper litemallCategoryMapper;
@Autowired
private LitemallGrouponRulesMapper litemallGrouponRulesMapper;
@Autowired
private LitemallBrandMapper litemallBrandMapper;
@Autowired
private LitemallGoodsMapper litemallGoodsMapper;
@Autowired
private LitemallTopicMapper litemallTopicMapper;
@Override
public MobileHomeVo getHomeIndex() {
MobileHomeVo vo = new MobileHomeVo();
//广告数据列表
vo.setBanner(litemallAdMapper.selectAdList());
//分类引导列表
vo.setChannel(litemallCategoryMapper.selectLevel1List());
//团购
vo.setGrouponList(litemallGrouponRulesMapper.selectGroupOnRulesList());
//品牌商
vo.setBrandList(litemallBrandMapper.selectBrandList(4));
//新品
LitemallGoods litemallGoods = new LitemallGoods();
litemallGoods.setIsNew(true);//条件
litemallGoods.setLimit(6);
vo.setNewGoodsList(litemallGoodsMapper.selectGoodsList(litemallGoods));
//热卖
litemallGoods.clear();
litemallGoods.setIsHot(true);//条件
litemallGoods.setLimit(6);
vo.setHotGoodsList(litemallGoodsMapper.selectGoodsList(litemallGoods));
//主题
vo.setTopicList(litemallTopicMapper.selectTopicList(4));
return vo;
}
}
商品加载
/**
* 商品服务
*/
@RestController
@RequestMapping("/wx/goods")
@Validated
public class WxGoodsController {
private final Log logger = LogFactory.getLog(WxGoodsController.class);
/**
* 根据条件搜素商品
* <p>
* 1. 这里的前五个参数都是可选的,甚至都是空
* 2. 用户是可选登录,如果登录,则记录用户的搜索关键字
*
* @param categoryId 分类类目ID,可选
* @param brandId 品牌商ID,可选
* @param keyword 关键字,可选
* @param isNew 是否新品,可选
* @param isHot 是否热买,可选
* @param userId 用户ID
* @param page 分页页数
* @param limit 分页大小
* @param sort 排序方式,支持"add_time", "retail_price"或"name"
* @param order 排序类型,顺序或者降序
* @return 根据条件搜素的商品详情
*/
@GetMapping("list")
public Object list(
Integer categoryId,
Integer brandId,
String keyword,
Boolean isNew,
Boolean isHot,
@LoginUser Integer userId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer limit,
@Sort(accepts = {"add_time", "retail_price", "name"}) @RequestParam(defaultValue = "add_time") String sort,
@Order @RequestParam(defaultValue = "desc") String order) {
//添加到搜索历史
if (userId != null && !StringUtils.isEmpty(keyword)) {
LitemallSearchHistory searchHistoryVo = new LitemallSearchHistory();
searchHistoryVo.setKeyword(keyword);
searchHistoryVo.setUserId(userId);
searchHistoryVo.setFrom("wx");
searchHistoryService.save(searchHistoryVo);
}
//查询列表数据
List<LitemallGoods> goodsList = goodsService.querySelective(categoryId, brandId, keyword, isHot, isNew, page, limit, sort, order);
// 查询商品所属类目列表。
List<Integer> goodsCatIds = goodsService.getCatIds(brandId, keyword, isHot, isNew);
List<LitemallCategory> categoryList = null;
if (goodsCatIds.size() != 0) {
categoryList = categoryService.queryL2ByIds(goodsCatIds);
} else {
categoryList = new ArrayList<>(0);
}
PageInfo<LitemallGoods> pagedList = PageInfo.of(goodsList);
Map<String, Object> entity = new HashMap<>();
entity.put("list", goodsList);
entity.put("total", pagedList.getTotal());
entity.put("page", pagedList.getPageNum());
entity.put("limit", pagedList.getPageSize());
entity.put("pages", pagedList.getPages());
entity.put("filterCategoryList", categoryList);
// 因为这里需要返回额外的filterCategoryList参数,因此不能方便使用ResponseUtil.okList
return ResponseUtil.ok(entity);
}
}
3.6 分类
@RestController
@RequestMapping("/wx/category")
public class CategoryController {
@Autowired
private MobileCategoryService mobileCategoryService;
@RequestMapping("/index")
public Object getIndex(){
Object data = mobileCategoryService.getCategoryIndex();
return ResponseUtils.ok(data);
}
@RequestMapping("/current")
public Object getCategory(Integer id){
Object data = mobileCategoryService.getCategoryAndSub(id);
return ResponseUtils.ok(data);
}
}
@Service
public class MobileCategoryServiceImpl implements MobileCategoryService {
@Autowired
private LitemallCategoryMapper litemallCategoryMapper;
@Override
public MobileCategoryVo getCategoryIndex() {
MobileCategoryVo vo = new MobileCategoryVo();
vo.setCategoryList(litemallCategoryMapper.selectLevel1ListBlob());
if(!vo.getCategoryList().isEmpty()){
vo.setCurrentCategory(vo.getCategoryList().get(0));
vo.setCurrentSubCategory(litemallCategoryMapper.selectSubListByPid(vo.getCurrentCategory().getId()));
}
return vo;
}
@Override
public MobileCategoryVo getCategoryAndSub(Integer id) {
MobileCategoryVo vo = new MobileCategoryVo();
LitemallCategory litemallCategory = litemallCategoryMapper.selectByPrimaryKey(id);
vo.setCurrentCategory(litemallCategory);
if(litemallCategory != null){
vo.setCurrentSubCategory(litemallCategoryMapper.selectSubListByPid(litemallCategory.getId()));
}
return vo;
}
}
4 SpringMVC框架
4.1 请求参数的处理
json格式
json格式的请求参数的接收,注解在controller方法中添加对应的java对象参数,并在参数前加上@RequestBody注解.
@PostMapping("/admin/auth/login") public Object login(@RequestBody AdminAuth auth){ }
注意: json对象中的属性和java对象中的属性一一对应
普通form-data
两种方式:
一个一个接收
```java
@GetMapping("/list") public Object list(@RequestParam(value="page",required = false,defaultValue = "1") Integer page, @RequestParam(value="limit",required = false,defaultValue = "20")Integer limit, @RequestParam(value="sort",required = false,defaultValue = "add_time")String sort, @RequestParam(value="order",required = false,defaultValue = "desc")String order, Integer goodsSn, Integer goodsId, String name){
}
```
- @RequestParam注解
- value设置对应的请求参数名 - required设置这个请求参数是否必填 - defaultValue设置这个请求参数的默认值(required是false才有意义)
注意:没有加RequestParam注解的参数,方法参数名必须和请求参数名一一对应
使用一个java实体对象封装
multipart/form-data(带文件上传)
@PostMapping("/create") public Object create(MultipartFile file){//MultipartFile类型的参数用来接收上传的文件 }
- MultipartFile
- getContentType
- getOriginalName
- getSize
- getBytes
- transferTo
- MultipartFile
还有一种特殊的路径参数
- 在路径中使用占位符声明路径参数
- 使用@PathVariable注解读取路径参数值
- 在调用后端接口时,路径中给路径参数设置具体的值
@GetMapping("/detail/{id}") public Object detail(@PathVariable("id") Integer id){ logger.info("id:" + id); return ResultUtils.success(); }
http://localhost:8080/admin/goods/detail/161008
4.2 请求头的处理
读取请求头中的token令牌
@PostMapping("/admin/auth/info") public Object getAdminInfo(@RequestHeader String token){ }
4.3 资源处理器ResourceHandler
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(FileUploadUtils.storageProfile + "/**")
.addResourceLocations("file:"+FileUploadUtils.saveDir+"/");
}
}
5. Mybatis框架
5.1 参数映射
CRUD增上改查语句都可能牵扯参数问题.
Mapper接口中方法中定义参数
分为下边三种情况:
只有一个参数并且是简单值类型的参数(基本数据类型/字符串/日期)
public AuthAdmin findAdminByUsername(String username);
只有一个参数但是是java对象类型或者Map类型
public int insertGoods(MallGoods mallGoods);
多个参数
public List<MallGoods> selectGoodslist(@Param("sort") String sort, @Param("order") String order,@Param("goodsSn")Integer goodsSn, @Param("goodsId")Integer goodsId, @Param("name")String name);
- 每个参数都必须用@Param注解设置一个名字
Mapper映射文件中sql语句要使用参数
首先在sql中parameterType指定参数类型. 要注意的是如果这里指定的是自定义的java类型,需要全类名;或者在配置文件中配置别名,就可以使用简单类名.
mybatis.type-aliases-package=com.edu.mall.admin.domain
然后上边三种情况sql语句中参数的使用方法:
只有一个参数并且是简单值类型的参数
sql语句中使用时直接 : #{任意名字}
<select id="selectAdminByUsername" parameterType="string" resultMap="adminBaseMap"> select id,username,password,last_login_ip,last_login_time,avatar, add_time,update_time,deleted,role_ids from litemall_admin where username=#{username} </select>
只有一个参数但是是java对象类型或者Map类型
sql语句中使用时: #{}中直接写参数对象中的属性名就可以访问属性值.(map中的key)
<insert id="insertGoods" parameterType="MallGoods"> insert into litemall_goods(id,goods_sn,name,category_id,brand_id,gallery,keywords,brief,is_on_sale,sort_order, pic_url,share_url,is_new,is_hot,unit,counter_price,retail_price,detail,add_time,update_time) values(#{id},#{goodsSn},#{name},#{categoryId},#{brandId},#{gallery},#{keywords},#{brief},#{isOnSale},#{sortOrder}, #{picUrl},#{shareUrl},#{isNew},#{isHot},#{unit},#{counterPrice},#{retailPrice},#{detail},#{addTime},#{updateTime}) </insert>
多个参数
不用设置parameterType
使用参数时,#{}中必须使用@Param注解设置的名字
5.2 结果映射
结果映射只有select查询语句需要;增删改不需要resultType或者resultMap。
结果映射分为resultType和resultMap:
resultType
如果查询结果是简单值类型,直接使用resultType指定数据类型即可.
如果查询结果要封装为java实体对象并且查询结果的列名和java属性都一一对应,直接resultType指定java类型即可.
<select id="selectAdminUserCount" resultType="int"> select count(id) from litemall_admin </select>
resultMap
查询结果要封装为java实体对象但是查询结果的列名和java属性并不能一一对应,那么需要自己定义这个映射关系.
<!-- 定义一个resultMap,映射数据库列名和java实体类属性名 --> <resultMap id="adminBaseMap" type="MallAdmin"> <!-- 主键字段用id --> <id column="id" property="id" /> <!-- 其他字段用result --> <result column="username" property="username" /> <result column="password" property="password" /> <result column="last_login_ip" property="lastLoginIp" /> <result column="last_login_time" property="lastLoginTime" /> <result column="avatar" property="avatar" /> <result column="add_time" property="addTime" /> <result column="update_time" property="updateTime" /> <result column="deleted" property="deleted" /> <result column="role_ids" property="roleIds" /> </resultMap> <!-- resultMap属性:应用上边定义的resultMap--> <select id="selectAdminByUsername" parameterType="string" resultMap="adminBaseMap"> select id,username,password,last_login_ip,last_login_time,avatar, add_time,update_time,deleted,role_ids from litemall_admin where username=#{username} </select>
5.3 动态sql
where
<where> <if test="goodsId!=null"> and id=#{goodsId} </if> <if test="goodsSn!=null"> and goods_sn=#{goodsSn} </if> <if test="name!=null and name!=''"> name like concat('%',#{name},'%') </if> </where>
set
if
forEach
where id in( <foreach collection="list" item="id" separator=","> #{id} </foreach> )
5.4 #{}和${}
{} sql中需要一个value值的地方,才能使用#{}
${} sql中如果只是拼接一段文本,可以使用${}
order by ${sort} ${order}
5.5 pagehelper分页插件
依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.5</version> </dependency>
配置
pagehelper.helper-dialect=mysql
使用
//在执行查询列表的语句之前设置分页的页号和页面行数 PageHelper.startPage(page, limit); List<MallGoods> mallGoods = adminGoodsMapper.selectGoodslist(sort, order, goodsSn, goodsId, name); //查询返回的list集合其实是一个Page,page对象中有分页相关的数据 Page<MallGoods> page = (Page<MallGoods>) mallGoods;//强转为Page data.put("total", page.getTotal()); data.put("pages", page.getPages()); data.put("limit", page.getPageSize()); data.put("page", page.getPageNum()); data.put("list", page.getResult());
6. 日志
在应用开发中经常需要输出各种日志,可以使用第三方的日志包。
常用的日志组件有:
- logging
- log4j/log4j2
- logback
- 等等
6.1 配置
# 日志输出级别(默认是info)(debug ---> info ---> warn ---> error)
logging.level.web=debug
logging.level.com.edu.mall.admin=debug
6.2 输出日志
//Logger对象用来输出日志
Logger logger = Logger.getLogger("adminAuthController");
//输出
logger.info("日志内容");
7. 加密
md5加密:
jdk
spring
/** * 加密 * @param source 指定要加密的明文 * @return 返回密文 */ public static String encode(String source){ return DigestUtils.md5DigestAsHex(source.getBytes()); }
8. Token令牌
为了实现一种有状态的登陆会话,需要在登录成功的时候给每个用户发一个token令牌,当这个用户会话再发请求时必须携带自己的令牌,这样服务端就知道这次请求是属于哪个用户会话.
jwt
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.4.1</version> </dependency>
生成和验证token
```java
public class JwtHelper { // 秘钥 static final String SECRET = "Easymall-Token"; // 签名是有谁生成 static final String ISSUSER = "EASYMALL"; // 签名的主题 static final String SUBJECT = "this is easymall token"; // 签名的观众 static final String AUDIENCE = "MOBILEWEBAPP";
public String createToken(Integer userId){
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
Map<String, Object> map = new HashMap<String, Object>();
Date nowDate = new Date();
// 过期时间:2小时
Date expireDate = getAfterDate(nowDate,0,0,0,2,0,0);
map.put("alg", "HS256");
map.put("typ", "JWT");
String token = JWT.create()
// 设置头部信息 Header
.withHeader(map)
// 设置 载荷 Payload
.withClaim("userId", userId)
.withIssuer(ISSUSER)
.withSubject(SUBJECT)
.withAudience(AUDIENCE)
// 生成签名的时间
.withIssuedAt(nowDate)
// 签名过期的时间
.withExpiresAt(expireDate)
// 签名 Signature
.sign(algorithm);
return token;
} catch (JWTCreationException exception){
exception.printStackTrace();
}
return null;
}
public Integer verifyTokenAndGetUserId(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256(SECRET);
JWTVerifier verifier = JWT.require(algorithm)
.withIssuer(ISSUSER)
.build();
DecodedJWT jwt = verifier.verify(token);
Map<String, Claim> claims = jwt.getClaims();
Claim claim = claims.get("userId");
return claim.asInt();
} catch (JWTVerificationException exception){
// exception.printStackTrace(); } return 0; }
public Date getAfterDate(Date date, int year, int month, int day, int hour, int minute, int second){
if(date == null){
date = new Date();
}
Calendar cal = new GregorianCalendar();
cal.setTime(date);
if(year != 0){
cal.add(Calendar.YEAR, year);
}
if(month != 0){
cal.add(Calendar.MONTH, month);
}
if(day != 0){
cal.add(Calendar.DATE, day);
}
if(hour != 0){
cal.add(Calendar.HOUR_OF_DAY, hour);
}
if(minute != 0){
cal.add(Calendar.MINUTE, minute);
}
if(second != 0){
cal.add(Calendar.SECOND, second);
}
return cal.getTime();
}
} ```